将异步调用包装到同步阻塞线程中?

时间:2011-12-20 22:24:50

标签: objective-c ios multithreading asynchronous synchronous

我正在编写一个iOS模块,它当前以异步方式发送电子邮件(使用委托)。它使用SKPSMTPMessage,效果很好。我的问题是客户希望代码完全阻止线程,直到电子邮件发送(或无法发送)。因此,他们基本上要求同步解决方案,当前它将尝试发送电子邮件,然后在发送电子邮件之前从该代码块返回。

因此,我没有尝试以同步方式重写SKPSMTPMessage代码(似乎没有任何同步选项),我希望找到一些方法来包装异步代码块。它自己的线程,也许让主线程等待它完全结束(委托和所有)。

我尝试了一些使用NSOperationNSThread的不同方法,但也许我没有做正确的事情,因为每当我尝试阻止主线程时,异步委托调用仍然不会出现完成(他们回到主线程或什么?)。

任何信息甚至是其他想法。

PS~我意识到这有点倒退了。在大多数情况下,异步似乎是要走的路,但这是一个特例,客户有理由想要它。

编辑:感谢所有输入。正如其中一个答案所示,我最后只是使用了一个等待代表返回的while循环,让runLoop继续如此:

while( ![messageDelegate hasFinishedOrFailed] ){
    // Allow the run loop to do some processing of the stream
    [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
}

4 个答案:

答案 0 :(得分:24)

我会尝试使用dispatch信号量。来自dispatch_semaphore_create(3)的手册页:

dispatch_semaphore_t sema = dispatch_semaphore_create(0);

dispatch_async(queue, ^{
    foo();
    dispatch_semaphore_signal(sema);
});

bar();

dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
dispatch_release(sema);
sema = NULL;

dispatch_semaphore_wait()的来电将一直停止,直到dispatch_semaphore_signal()的来电完成。

答案 1 :(得分:4)

我不相信没有修改SKPSMTPMessage就可以做到这一点。该类实际上并没有使用单独的线程;相反,它使用NSStream in concert with the thread's run loop来避免阻止:

[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
                           forMode:NSRunLoopCommonModes];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
                            forMode:NSRunLoopCommonModes];

该流充当run loop的输入源;然后,运行循环可以自由处理其他事件,直到流发生某些事情,此时流会通知其委托。一切仍然在原始(主)线程上发生;如果你试图自己阻止它,你也会阻止运行循环,它将无法对流做任何事情。

其他人已经指出,阻止主线程是一个坏主意;除了UX问题,系统可能会终止任何长时间不响应事件的应用程序。也就是说,可以将整个消息设置放入后台,为其提供自己的运行循环,并使用GCD阻止主线程。不幸的是,我想不出一种方法让代表在没有民意调查的情况下发出信号。

dispatch_queue_t messageQueue;
messageQueue = dispatch_queue_create("com.valheru.messageQueue", NULL);

// dispatch_sync blocks the thread on which it's called
dispatch_sync(messageQueue, ^{
    [messageManager tryToDeliverMessage];
});
dispatch_release(messageQueue);

tryToDeliverMessage看起来像:

- (void) tryToDeliverMessage {
    // Create the message and let it run...

    // Poll a flag on the delegate
    while( ![messageDelegate hasFinishedOrFailed] ){
        // Allow the run loop to do some processing of the stream
        [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
    }

    return;
}

答案 2 :(得分:2)

如果我误解了约束,请纠正我,但最终目标是阻止用户在发送电子邮件后导航?如果是这样,那么可能添加某种全屏进度覆盖视图作为窗口的子视图将是一个很好的解决方案。通过委托方法获得成功或失败回调时,禁用用户交互并删除视图。

答案 3 :(得分:0)

while(![messageDelegate hasFinishedOrFailed]){     //允许运行循环对流进行一些处理     [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]]; }

这将导致表视图有时无法滚动,并且警报视图无法关闭