首页 文章

如何让线程A等到线程B完成,然后继续线程A?

提问于
浏览
2

在线程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 回答

  • 0

    似乎有几个问题:

    • 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 方法被调用,因此你传递给 didFinishSelectorresponse 方法永远不会被调用 . 僵局 .

    • 但是,听起来你发现了另一个问题,即从 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 方法 . 死锁问题消失了,你的代码效率更高 .

  • 2

    你正在寻找的东西叫做信号量 . 以下是插入该术语的问题的链接:Objective-C Sempaphore Discussion

    顺便说一句,“信号量”这个词意味着交通信号灯 .

  • 0

    另外,你可以用信号量来实现这种功能 . 逻辑很简单:有一段时间,在你的线程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]];
    }
    
  • 0

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

相关问题