Почему для 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.