Почему для NSOperationQueue.mainQueue.maxConcurrentOperationCount установлено значение 1

Причиной этого вопроса являются реакции на этот вопрос.

Я понял, что понимание проблемы не полностью там, а также причина вопроса в первую очередь. Поэтому я пытаюсь свести причину другого вопроса к этому, по сути.

Сначала небольшое предисловие и немного истории, я знаю, что NSOperation(Queue) существовал до GCD, и они были реализованы с использованием потоков перед очередями отправки.

Следующее, что вам нужно понять, это то, что по умолчанию, что означает отсутствие "ожидающих" методов, используемых в операциях или очередях операций (просто стандартное "addOperation:"), основной метод NSOperation выполняется в основной очереди NSOperationQueue асинхронно (например, dispatch_async ()).

В заключение моего предисловия я подвергаю сомнению цель установки NSOperationQueue.mainQueue.maxConcurrentOperationCount равной 1 в этот день и возраст, теперь, когда нижележащий Queue фактически является основной последовательной очередью GCD (например, возвращением dispatch_get_main_queue ()).

Если NSOperationQueue.mainQueue уже выполняет основные методы своей операции последовательно, зачем вообще беспокоиться о maxConcurrentOperationCount?

Чтобы увидеть, что проблема установлена ​​в 1, см. Пример в ссылочном вопросе.

1 ответ

Он установлен на 1, потому что нет причин устанавливать его на что-либо еще, и, вероятно, немного лучше оставить его на 1, по крайней мере, по трем причинам, о которых я могу подумать.

Причина 1

Так как NSOperationQueue.mainQueue"s underlyingQueue является dispatch_get_main_queue(), который является серийным, NSOperationQueue.mainQueue фактически последовательный (он никогда не сможет запустить более одного блока за раз, даже если его maxConcurrentOperationCount были больше, чем 1).

Мы можем проверить это, создав собственную NSOperationQueueпомещая последовательную очередь в свою underlyingQueue целевая цепочка и настройка ее maxConcurrentOperationCount к большому количеству.

Создайте новый проект в Xcode, используя шаблон macOS > Cocoa App с языком Objective-C. Заменить 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

NSOperationQueue подчиняется до maxConcurrentOperationCount блокирует его underlyingQueue одновременно. Так как mainQueue.underlyingQueue является последовательным, только один из этих блоков может работать одновременно. После отправки этих блоков может быть слишком поздно использовать -[NSOperation cancel] сообщение об отмене соответствующих операций. Я не уверен; это деталь реализации, которую я не полностью изучил. В любом случае, если уже слишком поздно, это прискорбно, так как это может привести к трате времени и энергии аккумулятора.

Причина 3

Как упомянуто с причиной 2, NSOperationQueue подчиняется до maxConcurrentOperationCount блокирует его underlyingQueue одновременно. поскольку mainQueue.underlyingQueue является последовательным, только один из этих блоков может выполняться одновременно. Другие блоки и любые другие ресурсы dispatch_queue_t использует, чтобы отследить их, должен сидеть сложа руки, ожидая их очереди, чтобы бежать. Это пустая трата ресурсов. Не большая трата, но тем не менее. Если mainQueue.maxConcurrentOperationCount установлен в 1, он будет отправлять только один блок underlyingQueue в то же время предотвращая бесполезное распределение ресурсов GCD.

Другие вопросы по тегам