Почему основной цикл выполнения приложения и обработка ввода не потребляют процессорное время?

Я пытаюсь лучше понять циклы выполнения применительно к приложениям Mac (NSRunLoop), но это может быть и более общий вопрос. В документации NSRunLoop говорится:

... ваш код обеспечивает цикл while или for, который управляет циклом выполнения. В вашем цикле вы используете объект цикла выполнения для «запуска» кода обработки событий, который получает события и вызывает установленные обработчики.

В документах есть такой пример кода:

BOOL shouldKeepRunning = YES;
NSRunLoop *theRL = [NSRunLoop currentRunLoop];
while (shouldKeepRunning && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);

Таким образом, код продолжает вызывать цикл выполнения до тех пор, пока не будет решено, что его следует завершить. Метод -runMode:beforeDate: «Запускает цикл один раз, блокируя ввод в указанном режиме до указанной даты». А еще есть метод -run, который "Помещает приемник в постоянный цикл, в течение которого он обрабатывает данные со всех подключенных источников ввода".

Как возможно, что повторный вызов цикла выполнения (или вызов -run, который, похоже, делает это сам) не потребляет ресурсы ЦП? Приложение Cocoa может бездействовать в фоновом режиме, пока выполняется его основной цикл выполнения, и потреблять нулевое (или почти нулевое) процессорное время.

А в -runMode:beforeDate: как цикл выполнения может блокироваться до тех пор, пока не будет получен ввод или не сработают таймеры без опроса и потребления ЦП?


person jtbandes    schedule 13.03.2012    source источник


Ответы (1)


Это был хороший повод заглянуть внутрь Mac OS X! К счастью, соответствующие части Core Foundation имеют открытый исходный код.

Обычный ответ на вопрос "Как программа может ждать X, не загружая ЦП?" "Ядро сделало это". В этом случае запуск цикла выполнения на самом деле означает просто сообщить ядру, чего вы ждете, а затем позволить контексту ядра переключиться. В этом случае цикл выполнения проводит большую часть своего времени в mach_msg с флагом MACH_RCV_MSG, что на самом деле является просто системным вызовом ядра, который заканчивается планированием запуска другого потока. В конце концов происходит кое-что интересное: сообщение Mach отправляется на порт Mach, ядро ​​пробуждает заблокированный поток и доставляет сообщение. Программы видят это как возврат функции mach_msg.

Есть множество скрытых способов получить сообщение Mach, отправленное вам. Например, если вы настроите NSTimer, это, вероятно, сработает до системного вызова mk_timer_arm, который просто вызывает отправку сообщения Mach куда-то через определенное время. Цикл выполнения, таким образом, является не столько причудливым бесконечным циклом, сколько диспетчером и сопоставлением между ядром и платформами Cocoa (или Core Foundation).

Само собой разумеется, что если ядро ​​приостановило ваш поток из-за того, что вы ожидаете сообщения, то вы на самом деле не используете процессорное время, поэтому ваше приложение выглядит бездействующим. Зачем возиться с бесконечным циклом, если у вас есть современное ядро?

person John Calsbeek    schedule 21.08.2012