如何让线程a等待线程B完成,然后继续线程a



在线程a中,我调用异步服务,该服务运行在线程b中,该服务一旦完成调用委托方法。我希望线程A等待线程B完成。我使用了NSCondition

这是我的设置(跳过不重要的部分):

-(void)load
{
self.someCheckIsTrue = YES;
self.condition = [[NSCondition alloc] init];
[self.condition lock];
NSLog(@"log1");
Service *service = // set up service
[service request:url delegate:self didFinishSelector:@selector(response:)];
while (self.someCheckIsTrue)
[self.condition wait];
NSLog(@"log3");
[self.condition unlock];
}
-(void)response:(id)data
{
NSLog(@"log2");
[self.condition lock];
self.someCheckIsTrue = NO;
// do something with the response, doesn't matter here
[self.condition signal];
[self.condition unlock];
} 

由于某种原因,只打印"log1",而不打印"log2"one_answers"log3"。我想这就是为什么委托方法响应被"服务线程"调用,也就是线程B,而load被线程a调用

我也尝试了一个信号量,但也不起作用。下面是代码:

-(void)load
{        
NSLog(@"log1");
Service *service = // set up service
self.sema = dispatch_semaphore_create(0);
[service request:url delegate:self didFinishSelector:@selector(response:)];
dispatch_semaphore_wait(self.sema, DISPATCH_TIME_FOREVER);
NSLog(@"log3");
}
-(void)response:(id)data
{
NSLog(@"log2");
// do something with the response, doesn't matter here
dispatch_semaphore_signal(self.sema);
} 

我怎样才能使它工作?

似乎有几个问题:

  1. 在您的NSCondition示例中,load正在做锁,并且不会解锁,直到response设置一些状态变量。但是response不可能做到这一点,因为它也试图做一个锁(根据锁的性质,它将阻塞,直到另一个线程释放它的锁)。

  2. 此外,load正在启动service请求(其详细信息您还没有与我们共享),但根据您描述的行为(即,没有看到"log2"),我猜这个服务请求被安排在与load相同的线程上运行。(通常,如果一个服务运行在其他运行循环/队列/线程上,你会在该服务开始时看到一个参数,它会显式地说明这一点。)但是,如果load被冻结,等待来自另一个线程的信号,则服务将无法启动。你必须分享service请求性质的一些细节,以便我们进一步评论。

  3. 在评论中,您描述了使用GDataServiceGoogle。在你最初的问题中,你建议这个服务运行在一个单独的线程上。但是当我查看他们的一个示例应用程序时,我在NSURLConnectionDataDelegate方法中放置了一个断点,并且它正在主线程上被调用(实际上,它使用"当前"线程,并且因为示例从主线程启动它,所以NSURLConnectionDataDelegate调用在主线程上)。这证实了我之前的观点。

    (顺便说一下,这一点也不奇怪。许多基于NSURLConnectionDataDelegate的实现使用主队列进行网络连接。我不喜欢这种做法,但它使他们不必为网络活动创建另一个运行循环。它们只是假设你不会阻塞主队列。)

    但是如果你在调用服务的线程上有一些锁或信号量,这将阻止NSURLConnectionDataDelegate方法被调用,因此你作为didFinishSelector传递的response方法将永远不会被调用。死锁。

  4. 但是,听起来你发现了另一个问题,那就是从你的NSOperation发起服务调用将导致服务的内部NSURLConnectionDataDelegate调用不会被调用。这是NSURLConnection从后台队列调用的一个常见问题,通常通过(a)在一个专用线程中调度网络连接和它自己的运行循环来解决;(b)在[NSRunLoop mainRunLoop]中安排NSURLConnection;或者(c)为操作创建自己的运行循环。你已经成功地识别了,因为这个GDataServiceGoogle服务没有公开一个接口来控制哪个运行循环被使用,你将不得不选择选项(c)。这可能是最不优雅的解决方案,但考虑到GDataServiceGoogle的限制,这可能是你能做的最好的。

  5. 你问:

我怎么才能让这个工作?

虽然我在下面描述了几个解决方案,但最关键的观察是,您根本不应该使用信号量、锁或紧密的while循环。这些都代表了对处理这些异步请求的正确方式的误解:不是"等待"服务请求完成,而是在它完成时通知您(当您的response方法将被调用时)。删除所有的信号量、锁和紧密的while循环,并将您想要在"log3"中执行的任何逻辑移到response方法中。

了解了这些,更一般地考虑死锁,这里有两个观察结果:

  1. 确保将你的服务安排在一些不会被锁定的线程上。例如,您经常会看到第三个服务专用线程/队列,网络服务将在其上运行。或者有些人会将网络事务安排在主线程上(你永远不会阻塞),尽管我更喜欢专门的线程来处理这类事情。或者我看到有些人实际上在load例程中调用执行runloop(但我认为这是一个可怕的做法)。但是,你不能让load执行阻塞等待或其他功能,并让它在同一个线程上运行你的服务,所以要确保服务在其他线程上运行,并有自己的runloop。

  2. 如果要使用锁来同步对关键变量的访问,请确保两个线程都没有充分的理由长时间持有锁。尝试将锁的持续时间(如果有的话)最小化到尽可能小的代码部分,也就是说,只是在需要更新一些相互访问的资源的时候,但要尽快释放锁。但是,在dispatch_semaphore_wait或永久while循环周围设置锁通常会出现问题。

  3. 更激进地说,您可能会问是否有可能重构代码以完全消除锁和信号量。(参见并发编程指南中的消除基于锁的代码。)有时这是不实际的,但是串行队列(或并发队列上的障碍)已经消除了许多我过去依赖锁和信号量的情况。

就像我上面说的,我认为正确的解决方案是远离"等待服务完成"的模型,而只依赖服务在完成时调用response方法。死锁问题消失了,你的代码效率也大大提高了。

您正在寻找的东西称为信号量。这里是你的问题的链接,其中插入了这个术语:Objective-C semaphore Discussion

顺便说一句,"信号量"这个词就是红绿灯的意思。

或者,您可以使用信号量来实现此功能。逻辑很简单:有一段时间,等待你的线程a和在你的线程B,一旦你想线程a被释放,只需调用dispatch_semaphore_signal(semaphore);

这里有一个例子,我用来等待在restkit中的回调。你可以很容易地适应它。

//create the semaphore
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[objectManager.HTTPClient deletePath:[address addressURL] parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
//some code here, executed in background
dispatch_semaphore_signal(semaphore); //releasing semaphore
}failure:^(AFHTTPRequestOperation *operation, NSError *error) {
//some other code here
dispatch_semaphore_signal(semaphore); //releasing semaphore
}];
//holds the thread until the dispatch_semaphore_signal(semaphore); is send
while (dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW))
{
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:10]];
}

只需在任何全局队列上使用简单的dispatch_sync,通过您的服务逻辑传递块

相关内容

最新更新