为什么NSOperationQueue.mainQueue.maxConcurrentOperationCount设置为1

时间:2018-01-04 01:18:58

标签: ios nsoperationqueue foundation libdispatch

这个问题的原因是因为对this question的反应。

我意识到问题的理解并不完全存在,而且首先是问题的原因。所以我试图将其他问题的原因归结为这个问题的原因。

首先是一个小前言和一些历史记录,我知道NSOperation(Queue)在GCD之前就存在了,并且它们是在调度队列之前使用线程实现的。

接下来需要了解的是,默认情况下,意味着没有“等待”方法用于操作或操作队列(只是标准的“addOperation:”),NSOperation的主要内容方法在NSOperationQueue的基础队列上执行异步例如dispatch_async())。

结束我的序言,我质疑在当天和时代将NSOperationQueue.mainQueue.maxConcurrentOperationCount设置为1的目的,现在底层的Queue实际上是主要的GCD串行队列(例如dispatch_get_main_queue的返回( ))。

如果NSOperationQueue.mainQueue已经连续执行了它的操作主要方法,为什么要担心maxConcurrentOperationCount?

要查看将其设置为1的问题,请参阅引用问题中的示例。

1 个答案:

答案 0 :(得分:2)

它被设置为1,因为没有理由将它设置为其他任何东西,并且可能稍微好一点将其设置为1,至少有三个我能想到的原因。

原因1

由于NSOperationQueue.mainQueue的{​​{1}}是underlyingQueue,它是串行的,dispatch_get_main_queue()实际上是串行的(它一次只能运行多个块,甚至如果NSOperationQueue.mainQueue大于1)。

我们可以通过创建我们自己的maxConcurrentOperationCount来检查这一点,在其NSOperationQueue目标链中放置一个串行队列,并将其underlyingQueue设置为一个大数字。

使用macOS>在Xcode中创建一个新项目; Cocoa App模板,语言为Objective-C。将maxConcurrentOperationCount实现替换为:

AppDelegate

如果你运行它,你会看到操作1直到操作0结束才开始,即使我将@implementation AppDelegate { dispatch_queue_t concurrentQueue; dispatch_queue_t serialQueue; NSOperationQueue *operationQueue; } - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { concurrentQueue = dispatch_queue_create("q", DISPATCH_QUEUE_CONCURRENT); serialQueue = dispatch_queue_create("q2", nil); operationQueue = [[NSOperationQueue alloc] init]; // concurrent queue targeting serial queue //dispatch_set_target_queue(concurrentQueue, serialQueue); //operationQueue.underlyingQueue = concurrentQueue; // serial queue targeting concurrent queue dispatch_set_target_queue(serialQueue, concurrentQueue); operationQueue.underlyingQueue = serialQueue; operationQueue.maxConcurrentOperationCount = 100; for (int i = 0; i < 100; ++i) { NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"operation %d starting", i); sleep(3); NSLog(@"operation %d ending", i); }]; [operationQueue addOperation:operation]; } } @end 设置为100.这是因为目标链中有一个串行队列operationQueue.maxConcurrentOperationCount。因此,operationQueue.underlyingQueue实际上是连续的,即使其operationQueue不是1。

您可以使用代码尝试更改目标链的结构。你会发现,如果该链中的任何地方都有一个串行队列,那么一次只能运行一个操作。

但是如果您设置maxConcurrentOperationCount,并且operationQueue.underlyingQueue = concurrentQueue的目标设置为concurrentQueue,那么您将看到64个操作同时运行。要使serialQueue同时运行操作,以operationQueue开头的整个目标链必须是并发的。

由于主队列始终是串行的,underlyingQueue实际上始终是串行的。

实际上,如果将NSOperationQueue.mainQueue设置为除1以外的任何值,则无效。如果您在尝试更改它之后打印NSOperationQueue.mainQueue.maxConcurrentOperationCount,您会发现它仍然是1.我认为如果尝试更改它会提出一个断言会更好。默默无视改变它的企图更有可能导致混淆。

原因2

NSOperationQueue.mainQueue.maxConcurrentOperationCount同时向其NSOperationQueue提交最多maxConcurrentOperationCount个阻止。由于underlyingQueue是串行的,因此一次只能运行其中一个块。提交这些块后,使用mainQueue.underlyingQueue消息取消相应的操作可能为时已晚。我不确定;这是我尚未完全探索的实现细节。无论如何,如果为时已晚,那将是不幸的,因为它可能会浪费时间和电池电量。

原因3

正如原因2所述,-[NSOperation cancel]同时向其NSOperationQueue提交最多maxConcurrentOperationCount个阻止。由于underlyingQueue是串行的,因此一次只能执行其中一个块。其他块以及mainQueue.underlyingQueue用于跟踪它们的任何其他资源必须闲置,等待轮到他们。这是浪费资源。不是浪费,但浪费。如果dispatch_queue_t设置为1,则它一次只会向其mainQueue.maxConcurrentOperationCount提交一个块,从而阻止GCD无用地分配资源。