【问题标题】:Demystify NSOperation: concurrent vs non-concurrent and async pattern揭秘 NSOperation:并发与非并发和异步模式
【发布时间】:2012-06-02 14:34:59
【问题描述】:

是的,我知道。关于NSOperation 世界有很多问题和答案,但我仍然有一些疑问。我试图用一个两部分的问题来解释我的疑问。它们相互关联。

nsoperationqueue-and-concurrent-vs-non-concurrent 的 SO 帖子中,Darren 写道

“并发”操作本身就是并发的;它不需要 NSOperationQueue 为其创建一个线程。

但是稍微搜索了一下,我发现NSOperation,即使它被声明为并发(通过覆盖isConcurrent方法例如它返回YES),也可以添加到@987654327 @。这是什么意思?如果我将并发 NSOperation 添加到队列中,幕后发生了什么?相反,如果我按原样使用并发操作(不将其添加到队列中)会发生什么?

从 Apple 文档中提取的注释很清楚:

...操作队列忽略返回的值 isConcurrent 并始终从 单独的线程。 ...一般来说,如果你总是 使用带有操作队列的操作,没有理由做 他们是并发的。

然后,我对在NSOperation 中使用异步模式非常感兴趣。我发现 Dave Dribin (concurrent operations) 的教程很好。我明白了他帖子的整体含义。

您不能使用异步模式(例如,使用异步 NSURLConnection 请求),因为无法调用委托。当main 完成时,该操作被删除。解决方案是重写start 方法来控制操作生命周期......而处理运行循环可能会很痛苦。

现在,试图理解他的帖子,我怀疑是否需要在主线程中运行start 方法。

- (void)start
{
    if (![NSThread isMainThread])
    {
        [self performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:NO];
        return;
    }

    // other code here...
}

在处理异步 API 时,我们可以开始异步 在启动时调用主线程并保持操作运行直到 它完成了。

你能解释一下为什么吗?

提前谢谢你。

【问题讨论】:

    标签: objective-c asynchronous nsurlconnection nsoperation nsoperationqueue


    【解决方案1】:

    在我看来,NSOperationisConcurrent 属性名称容易混淆。它的真正意思是“异步”。也就是说,当它的-start被调用时,无论操作是否运行完成(异步),它是否会快速返回?还是直到操作运行完成(同步)才返回?

    正如 Apple 的文档所述,当操作排队到 NSOperationQueue 时,这并不重要,因为无论如何,队列都会在工作线程上调用它。如果它是同步的,那么该工作线程将专门用于该操作,直到它完成。如果是异步的,那么-start会在操作完成之前返回,工作线程可以继续做其他工作。

    问题是,异步-start 方法如何确保操作的工作继续进行? 可能需要产生一个单独的线程来完成这项工作,但这很愚蠢。最好让NSOperationQueue 处理线程。

    更有可能的是,它使用由外部事件驱动的运行循环源。 NSURLConnection 在其异步模式下就是这样的事情。但是,在这种情况下,它必须确保调度运行循环源的线程 a) 将继续存在,并且 b) 将运行其运行循环。也不能依赖NSOperationQueue 的工作线程来做。

    同样,您可以为每个此类操作创建自己的线程,专门用于停留并运行其运行循环,但这是不必要的,而且与让操作保持同步并对其进行排队没有任何优势.

    您已经知道将保留并运行其运行循环的一个线程是主线程。因此,通常最好将运行循环源安排在主线程的运行循环上。唯一需要注意的是,为了响应触发运行循环源处理程序的外部事件,您不要在主线程上执行任何长时间运行的工作。因此,例如,当NSURLConnection 使用接收到的数据调用您的委托方法时,您不会对该数据进行昂贵的计算——或者,如果必须,将这种昂贵的计算转移到另一个线程。

    另一种可能性,一种中间立场,是创建一个您自己的线程作为许多异步操作的工作线程。因此,不是使用主线程或每个操作的线程,而是使用单个线程,其工作只是停在其运行循环中。您的所有异步操作都会在该线程的运行循环中自行安排。不过,这种方法并没有太大的必要性或优势。

    【讨论】:

    • 肯,非常感谢您的回复。我读了三遍。有很多概念。 + 1 确实。最后一个问题,如果你能回答。对于 Dave 在主线程上执行 start 的方法,你有什么看法?为什么操作需要依赖主线程?谢谢。
    • 不客气。我试图解决这个问题。 NSURLConnection,在其异步模式下,依赖于运行循环。它调度一个(私有)运行循环源来监视底层套接字。当某些事件发生时(例如数据到达),运行循环源调用它的处理程序(这是NSURLConnection 内部的某个函数)。反过来,这可能会调用您的委托方法。如果操作没有被分流到主线程,那么它就不能确定调度它的运行循环是否会运行——它会处理事件。
    • 好的,我明白了。好的!感谢您的支持。
    【解决方案2】:

    我认为 Dave 在帖子中解决了他将 start 方法分流到主线程的动机:

    2009-09-13 更新:从 10.6 起不再适用。启动方法 从 10.6 开始,总是在后台线程上调用。正常工作 仅使用主线程和依赖运行循环的异步 API, 我们需要将工作分流到主线程。更多关于这个 后续帖子。

    【讨论】:

    • 谢谢你,克里斯。能给我一个更详细的答案吗?谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-28
    • 1970-01-01
    相关资源
    最近更新 更多