等待异步回调进行同步 [英] wait an async callback to sync

查看:103
本文介绍了等待异步回调进行同步的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想包装一个SDK异步api进行同步,代码看起来像这样:

I want to wrap a SDK async api to sync, code looks like this:

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;

我已经类似地阅读了这个问题 如何等待异步分配的块完成吗?

I already read the question similarly How do I wait for an asynchronously dispatched block to finish?

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

but, when it was call at UI thread, deadlock happen because the SDK callback run the block at UI thread too.

如何处理?谢谢.

推荐答案

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

If SDKPlatform is dispatching its completion block back to the main queue, then your approach of blocking the main thread until the completion block is called will certainly deadlock and there's not much you can do about that. But this semaphore approach to block the main thread so you can make an asynchronous method behave like a synchronous one is an inadvisable approach, anyway. You should actually embrace the asynchronous patterns and employ the completion block techniques in your own code.

该链接,我如何等待,说明了如何使用信号量使异步任务同步运行.可悲的是,这种技术被误用为令人震惊的频率.具体来说,在这种情况下,信号量不是您方案中的合适模式,因为信号量模式会阻塞主线程,这是我们在应用程序中永远都不应做的事情.

That link, How do I wait for an asynchronously dispatched block to finish?, illustrates how one can use semaphores to make asynchronous task run synchronously. Sadly, this technique is misused with alarming frequency. Specifically, in this case, the semaphore is not the appropriate pattern in your scenario because the semaphore pattern will block the main thread, something that we should never do within an app.

从背景的角度来看,信号灯技术在另一个线程中讨论的场景中很好,因为这是一个非常不同的技术问题.它是在测试框架而不是应用程序的特殊情况下使用的,并且在以下情况下使用:(a)测试本身必须在主线程上进行; (b)为使测试框架正常运行,它必须阻塞主线程,直到异步任务完成为止.此外,它也恰好在该测试方案中起作用,因为完成块不会发生在主队列上,从而避免了您遇到的死锁问题.

By way of background, the semaphore technique is fine in the scenario discussed in that other thread, because it's a very different technical problem. It is being use in a special situation of a testing framework, not an app, and in a situation where (a) the test, itself, must happen on the main thread; (b) for the testing framework to function, it must block the main thread until the asynchronous task completes. Furthermore, it also happens to work in that testing scenario, because the completion block does not take place on the main queue, avoiding the deadlock issue you're experiencing.

这三个条件都不符合您的情况.不建议在您的情况下使用信号量技术.

None of these three conditions hold in your case. To use the semaphore technique in your situation is inadvisable.

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

So, let's step back and look at your problem. I'm assuming that you have some method that looks like:

- (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;
}

即使您没有死锁问题,这仍然是错误的模式.您可能想要的是这样的东西:

Even if you didn't have your deadlock problem, this is still the wrong pattern. What you probably want is something like:

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

请注意,此函数具有void返回类型,但是isLoggedIn状态由完成块返回(并且应仅在完成块内使用,如下所示:

Note, this function has a void return type, but rather the isLoggedIn state is returned by the completion block (and should be only used within the completion block, like so:

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

这篇关于等待异步回调进行同步的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆