1

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

我意识到对问题的理解并不完全存在,以及首先提出问题的原因。所以我试图将另一个问题的原因归结为这个问题的核心。

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

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

总结一下我的前言,我质疑在这个时代将 NSOperationQueue.mainQueue.maxConcurrentOperationCount 设置为 1 的目的,因为现在底层队列实际上是主要的 GCD 串行队列(例如 dispatch_get_main_queue() 的返回)。

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

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

4

1 回答 1

3

将其设置为 1 是因为没有理由将其设置为其他任何值,并且出于我能想到的至少三个原因,将其设置为 1 可能会稍微好一些。

原因一

因为NSOperationQueue.mainQueue's underlyingQueueisdispatch_get_main_queue()是串行的,NSOperationQueue.mainQueue实际上是串行的(它一次永远不能运行超过一个块,即使它maxConcurrentOperationCount大于 1)。

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

使用 macOS > Cocoa App 模板和语言 Objective-C 在 Xcode 中创建一个新项目。用这个替换AppDelegate实现:

@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

如果你运行这个,你会看到操作 1 直到操作 0 结束才开始,即使我设置operationQueue.maxConcurrentOperationCount为 100。发生这种情况是因为operationQueue.underlyingQueue. 因此operationQueue实际上是串行的,即使它maxConcurrentOperationCount不是 1。

您可以使用代码来尝试更改目标链的结构。您会发现,如果在该链中的任何位置都存在串行队列,则一次只能运行一个操作。

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

由于主队列始终是串行的,NSOperationQueue.mainQueue因此实际上始终是串行的。

事实上,如果你设置NSOperationQueue.mainQueue.maxConcurrentOperationCount为 1 以外的任何值,它都没有效果。如果您NSOperationQueue.mainQueue.maxConcurrentOperationCount在尝试更改它之后打印,您会发现它仍然是 1。我认为如果尝试更改它会引发断言会更好。默默地忽略改变它的尝试更有可能导致混乱。

原因 2

NSOperationQueuemaxConcurrentOperationCount同时向其提交最多块underlyingQueue。由于mainQueue.underlyingQueue是串行的,因此一次只能运行其中一个块。-[NSOperation cancel]一旦那些区块被提交,使用消息取消相应的操作可能已经来不及了。我不确定; 这是一个我还没有完全探索的实现细节。无论如何,如果为时已晚,那将是不幸的,因为这可能会导致时间和电池电量的浪费。

原因 3

如原因 2 所述,同时向其NSOperationQueue提交最多块。由于是串行的,因此一次只能执行其中一个块。其他块以及用于跟踪它们的任何其他资源必须闲置,等待轮到它们运行。这是对资源的浪费。不是很大的浪费,但还是很浪费。如果设置为 1,它一次只会向它提交一个块,从而防止 GCD 无用地分配资源。maxConcurrentOperationCountunderlyingQueuemainQueue.underlyingQueuedispatch_queue_tmainQueue.maxConcurrentOperationCountunderlyingQueue

于 2018-01-04T03:48:41.563 回答