2014-09-29 168 views
1

我想换一个SDK异步API同步,代码如下:等待一个异步回调同步

dispatch_semaphore_t sema = dispatch_semaphore_create(0); 
__block BOOL _isLogined; 
__block BOOL _isCallback = NO; 
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 
dispatch_async(queue,^{ 
    //Put your heavy code here it will not block the user interface 
    [[SDKPlatform defaultPlatform] SDKIsLogined:^(BOOL isLogined){ 
     _isLogined = isLogined; 
     _isCallback = YES; 
     dispatch_semaphore_signal(sema); 
    }]; 
}); 
while (!_isCallback) { 
    dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); 
} 
return _isLogined; 

我已经阅读问题同样 How do I wait for an asynchronously dispatched block to finish?

,但是,当它被调用在UI线程中,死锁发生是因为SDK回调在UI线程处运行该块。

如何处理?谢谢。

+0

除非我误解了一些与信号灯的方法等是完全不正确的,埃里克。你要做的是:(1)运行的代码(2)完成后,通知主线程,然后你可以做接下来的事情,这是绝对平常的,下面的例子! – Fattie 2014-09-29 06:28:37

+0

你需要增加你的信号计数“1”(使用“1”代替它的创建时间为0)。创建没有空间访问共享资源的信号量会在调用dispatch_semaphore_wait时导致死锁。 – user298261 2015-06-04 20:32:42

回答

0

如果SDKPlatform正在将其完成块分派回主队列,那么阻止主线程直到完成块被调用的方法肯定会死锁,并且您无法做到这一点。但是,这种信号量方法阻止主线程,所以您可以使异步方法像同步方法一样行事,无论如何,这是一种不可取的方法。实际上你应该拥抱异步模式,并在你自己的代码中使用完成块技术。

该链接How do I wait for an asynchronously dispatched block to finish?说明了如何使用信号量使异步任务同步运行。可悲的是,这种技术被误用于报警频率。具体而言,在这种情况下,信号量不是您场景中的适当模式,因为信号量模式会阻止主线程,这是我们在应用程序中永远不应该做的事情。

作为背景,信号量技术在其他线程中讨论的场景中很好,因为它是一个非常不同的技术问题。它被用于测试框架的特殊情况,而不是应用程序,以及(a)测试本身必须在主线程上发生的情况; (b)为使测试框架起作用,它必须阻塞主线程,直到异步任务完成。此外,它也适用于该测试场景,因为完成块不会在主队列上发生,从而避免了您遇到的死锁问题。

这三个条件都不适用于您的情况。在你的情况下使用信号量技术是不可取的。

所以,让我们退后一步,看看你的问题。我假设你有一些方法,看起来像:

- (BOOL) login 
{ 
    dispatch_semaphore_t sema = dispatch_semaphore_create(0); 
    __block BOOL _isLogined; 
    __block BOOL _isCallback = NO; 
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 
    dispatch_async(queue,^{ 
     //Put your heavy code here it will not block the user interface 
     [[SDKPlatform defaultPlatform] SDKIsLogined:^(BOOL isLogined){ 
      _isLogined = isLogined; 
      _isCallback = YES; 
      dispatch_semaphore_signal(sema); 
     }]; 
    }); 
    while (!_isCallback) { 
     dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); 
    } 
    return _isLogined; 
} 

即使你没有你的死锁问题,这仍然是错误的花样。你可能想要的是一样的东西:

- (void)loginWithCompletionHandler:(void (^)(BOOL isLoggedIn))completionHandler 
{ 
    [[SDKPlatform defaultPlatform] SDKIsLogined:^(BOOL isLoggedIn){ 
     if (completionHandler) { 
      completionHandler(isLoggedIn); 
     } 
    }]; 
} 

注意,这个功能有一个void返回类型,而是isLoggedIn状态由完成块返回(并应完成区块内仅使用,就像这样:

[self loginWithCompletionHandler:^(BOOL isLoggedIn) { 
    // feel free to use isLoggedIn here 
}]; 
// don't try to use isLoggedIn here