在线程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"和"log3" . 我假设这是为什么委托方法 response 被"service thread"调用,这是线程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);
}
我怎样才能让它发挥作用?
4 回答
似乎有几个问题:
在
NSCondition
示例中,load
正在执行锁定,并且在response
设置某个状态变量之前不会解锁 . 但是response
也可以't possibly get to that because, it'试图做一个锁(根据锁的性质,它会阻塞,直到另一个线程释放它的锁) .此外,
load
正在发起一个service
请求(其详细信息是您猜测此服务请求被安排在与load
相同的线程上运行 . (通常,如果某个服务正在某个其他的runloop / queue / thread上运行,那么'' d在该服务启动期间看到一个参数,使其显式化 . )但如果load
被冻结,等待来自另一个线程的信号,则服务赢得't commence. You' d必须共享service
请求性质的一些细节 . 我们进一步评论 .在评论中,您描述了使用GDataServiceGoogle . 在您的原始问题中,您建议此服务在单独的线程上运行 . 但是当我查看他们的一个示例应用程序时,我在NSURLConnectionDataDelegate方法中放置了一个断点,并且它在主线程上被调用(实际上,它使用"current"线程,并且因为示例从主线程启动它,
NSURLConnectionDataDelegate
调用在主线程上) . 这证实了我先前的观点 .(顺便说一句,这并不奇怪 . 很多基于
NSURLConnectionDataDelegate
的实现使用主队列进行网络连接 . 我只是假设你不会阻塞主队列 . )但是如果你在调用服务的线程上有一些锁或信号量,这将阻止
NSURLConnectionDataDelegate
方法被调用,因此你传递给didFinishSelector
的response
方法永远不会被调用 . 僵局 .NSOperation
启动服务调用将导致服务的内部NSURLConnectionDataDelegate
调用't get called. That'来自后台队列的NSURLConnection
调用常见问题,通常由(a)调度解决具有自己的运行循环的专用线程中的网络连接; (b)在[NSRunLoop mainRunLoop]
中安排NSURLConnection
;或(c)为操作创建自己的运行循环 . 并且你've successfully identified that because this GDataServiceGoogle service doesn'暴露了一个用于控制使用哪个运行循环的接口,你可能是最不优雅的解决方案,但考虑到GDataServiceGoogle的限制,它可能是你能做的最好的 .你问:
虽然我在下面描述了几个解决方案,但最关键的观察是你根本不应该使用信号量,锁或紧密的
while
循环 . 这些都代表了对处理这些异步请求的正确方法的误解:完成服务请求时不会"waiting",而是在完成时(当调用response
方法时)通知您 . 删除所有信号量,锁定和紧密的while
循环,并在"log3"移动你想要做的任何逻辑并将其放在response
方法中 .在我们身后,更普遍地考虑死锁,有几点意见:
确保在某个赢得的线程上安排您的服务't lock. For example, you' ll经常会看到运行网络服务的第三个服务专用线程/队列 . 或者有些人会在主线程(你永远不会阻止)上安排网络内容,虽然我更喜欢这种东西的专用线程 . 或者我已经看到有些人实际上在
load
例程中调用了执行runloop(但我认为这是一种可怕的做法) . 但是,你根本不能让load
执行阻塞等待或其他功能并让它在同一个线程上运行你的服务,所以确保该服务在其他一些具有自己的runloop的线程上运行 .如果您要使用锁来同步对关键变量的访问,请确保两个线程都没有充分理由长时间保持锁定 . 尝试将锁的持续时间(如果有的话)最小化到代码的最小可能部分,即只需要更新某些相互访问的资源的那些时间,但尽快释放该锁 . 但是锁定诸如
dispatch_semaphore_wait
或永久while
循环之类的东西会往往是有问题的 .更根本的是,您可能会问是否有可能重构代码以完全消除锁和信号量 . (请参阅“并发编程指南”中的Eliminating Lock-Based Code . )有时这不实用,但串行队列(或并发队列上的障碍)已经消除了我曾经依赖锁和信号量的许多情况 .
就像我上面所说的那样,我认为正确的解决方案是远离"wait for the service to finish"模型,只需依靠服务就可以在完成后调用
response
方法 . 死锁问题消失了,你的代码效率更高 .你正在寻找的东西叫做信号量 . 以下是插入该术语的问题的链接:Objective-C Sempaphore Discussion
顺便说一句,“信号量”这个词意味着交通信号灯 .
另外,你可以用信号量来实现这种功能 . 逻辑很简单:有一段时间,在你的线程A中等待 . 在你的线程B中,一旦你想要释放线程A,只需调用
dispatch_semaphore_signal(semaphore);
这是我用来等待restkit中的回调的一个例子 . 你可以很容易地适应它 .
只需在任何全局队列上使用简单的dispatch_sync,并使用您的服务逻辑传递块