繁体   English   中英

等待异步回调进行同步

[英]wait an async callback to sync

我想包装一个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;

我已经类似地阅读了问题, 如何等待异步调度的块完成?

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

怎么处理呢? 谢谢。

如果SDKPlatform将其完成块分派回主队列,那么您阻塞主线程直到完成块被调用的方法肯定会死锁,对此您无能为力。 但是这种信号量方法阻塞了主线程,因此无论如何,使异步方法表现得像同步方法一样是不明智的方法。 您实际上应该拥抱异步模式,并在自己的代码中采用完成块技术。

该链接, 我如何等待异步调度的块完成? ,说明了如何使用信号量使异步任务同步运行。 可悲的是,该技术被误用为令人震惊的频率。 具体来说,在这种情况下,信号量不是您方案中的适当模式,因为信号量模式将阻塞主线程,这是我们绝对不能在应用程序中执行的操作。

作为背景,信号灯技术在另一个线程中讨论的场景中很好,因为它是一个非常不同的技术问题。 它是在测试框架而不是应用程序的特殊情况下使用的,并且在以下情况下使用:(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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM