2

在查看我的代码时,我发现在很多地方我一直在假设调用[NSBlockOperationInstance start];将在主线程上启动此操作。我不知道我为什么会这样想,但我不应该这么肯定。我检查了文档,但找不到任何明确提及该块将在其上运行的线程。assert([NSThread isMainThread]);但是,在块的主体中​​断言确实每次使用都会通过start,所以我不确定这是否是巧合。有人对这将如何工作有更深入的了解吗?

我忘了提到 [op start] 正在主线程上被调用。

4

2 回答 2

12

好的,这完全取决于您调用 start() 的位置。虽然 NSBlockOperation 会将块分配给其他线程,但 start() 是同步的,并且在所有已分配给 NSBlockOperation 的块完成之前不会返回。

虽然 NSBlockOperation 将并发执行它给定的块,但 NSBlockOperation 本身不是并发的(即 isConcurrent 为假)。因此,根据文档,start() 将在 start() 的调用者的线程中完整执行。

由于调用 start() 的线程在所有块执行完毕后才会返回,因此让调用线程参与执行并发块的线程池是有意义的。这就是为什么您会看到一些块在调用 start() 的线程中执行。

如果您在主线程中看到一个块执行,那么您一定是从主线程调用它。

在相关说明中,如果您的 NSBlockOperation 包含单个块,则该块将始终在调用线程中执行。

请记住,如果您希望 NSOperation 完全并发,则必须在子类中实现适当的功能。

除此之外,您可以将任何 NSOperation 赋予 NSOperationQueue,它将同时执行,因为 NSOperation 被赋予一个队列,并且运行该操作的线程调用 start()。

就个人而言,除非我需要使用它的功能,否则我认为使用 NSBlockOperation 优于 dispatch_async() 没有任何优势。如果您只执行一个块,只需调用

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ });

如果您想利用 NSBlockOperation 的功能,但又不想在当前调用线程中等待它们完成,那么这样做仍然有意义...

// Add lots of concurrent blocks
[op addExecutionBlock:^{ /*whatever*/ }];
// Execute the blocks asynchronously
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [op start];
    // Now do what you want after all the concurrent blocks have completed...
    // Maybe even tell the UI
    dispatch_async(dispatch_get_main_queue(), ^{
        // Update the UI now that all my concurrent blocks have finished.
    });
});

编辑 要解决您对 tc 答案的评论...

如果你打电话

op = [NSBlockOperation blockOperationWithBlock:^{assert([NSThread isMainThread])}];
[op start];

从主线程,那么有一些保证,和一些高概率。

首先,您保证 [op start] 将在调用线程中运行完成。这是因为 NSBlockOperation 不会覆盖指定它不是并发操作的 NSOperation 的默认行为。

接下来,如果 NSBlockOperation 只有一个块,您很有可能会在调用线程中运行。第一个块在调用线程中运行的概率几乎相同。

但是,上述“概率”并不能保证(只是因为文档没有说明)。我想,一些工程师可能会找到一些理由将该单个块旋转到一个并发队列中,并且只是让调用线程加入另一个线程中正在执行的操作......但我非常怀疑这一点。

无论如何,也许你的困惑来自这样一个事实,即 NSBlockOperation 的文档说它同时执行块,它确实如此。但是,操作本身不是并发的,所以初始操作是同步的。它将等待所有块执行,并且它可能(或可能不)在调用线程上执行其中一些。

虽然不能保证,但我发现只有一个块的 NSBlockOperation 不太可能执行除了在调用线程上执行之外的任何操作。

于 2012-07-21T05:29:03.380 回答
2

文档特别说

添加到块操作的块以默认优先级分派到适当的工作队列。块本身不应对其执行环境的配置做出任何假设。

我怀疑以下内容会崩溃:

NSBlockOperation * op = [NSBlockOperation blockOperationWithBlock:^{ sleep(1); }];
[op addExecutionBlock:^{assert([NSThread isMainThread]); }];
[op start];

简单地执行块有什么问题?

于 2012-07-21T04:34:27.100 回答