Подтвердить что ты не робот

Нужны некоторые пояснения относительно очереди отправки, потока и NSRunLoop

Я знаю и понимаю следующие вещи:

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

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), {
 for (int i; i<10; i++) {
  doTask()
 }
})

Если я хочу отправить в очередь serial, я могу использовать

dispatch_async(dispatch_queue_create("my.serial.queue", nil) {
  ...
}

каждый раз только одна задача отправляется в поток и выполняется. Порядок - это FIFO.

===== Что меня смущает и не полностью понимает =======

  • Основной поток имеет NSRunLoop, задачи цикла в основном потоке. Мне интересно, что такое связь между диспетчерской очередью и циклом запуска? Могу ли я это понять, если при отправке задачи в основной поток основной поток NSRunLoop получает отправленную задачу и выполняет ее?

  • Как насчет глобальной очереди, отправляющей задачи на несколько потоков? Система iOS/OSX автоматически создает не только потоки, но также создает NSRunLoop для каждого потока? а затем цикл цикла в каждом потоке получает отправленную задачу из глобальной очереди и выполняет ее?

  • Кто знает эту тему? Вы знаете, что функция dispatch_async() и dispatch_sync() знает, к какому потоку относится задача отправки, или знает ли очередь , какой поток отправляет задачу?

  • Есть ли способ получить объект NSRunLoop потока (которому отправлена ​​задача) из очереди отправки программно? (этот вопрос связан с вопросом 3)

4b9b3361

Ответ 1

  • В цикле выполнения основного потока есть шаг, на котором он запускает любые блоки, помещенные в очередь в основной очереди. Вы можете найти этот ответ полезным, если хотите понять, что работает цикл выполнения.

  • GCD создает потоки для параллельных очередей. Нить не имеет цикла выполнения до тех пор, пока в первый раз что-то работающее в потоке не спросит цикл цикла потока, после чего система создает цикл цикла для потока. Однако цикл выполнения запускается только в том случае, если что-то в этом потоке запрашивает его запуск (путем вызова -[NSRunLoop run] или CFRunLoopRun или аналогичного). Большинство потоков, включая потоки, созданные для очередей GCD, никогда не имеют цикла выполнения.

  • GCD управляет пулом потоков, и когда ему нужно запустить блок (потому что он был добавлен в некоторую очередь), GCD выбирает поток, на котором запускается блок. Алгоритм выбора GCD-потоков в основном представляет собой деталь реализации, за исключением того, что он всегда выбирает основной поток для блока, который был добавлен в основную очередь. (Обратите внимание, что GCD также иногда использует основной поток для блока, добавленного в какую-либо другую очередь.)

  • Вы можете получить только цикл выполнения основного потока (используя +[NSRunLoop mainRunLoop] или CFRunLoopGetMain) или цикл выполнения текущего потока (используя +[NSRunLoop currentRunLoop] или CFRunLoopGetCurrent). Если вам нужен цикл выполнения какого-либо произвольного потока, вы должны найти способ вызвать CFRunLoopGetCurrent в этом потоке и передать его возвращаемое значение обратно через потоки безопасным, синхронизированным способом.

    Обратите внимание, что NSRunLoop интерфейс не является потокобезопасным, но CFRunLoop интерфейс является потокобезопасным, поэтому, если вам нужно получить доступ к другому циклу запуска потока, вы должны использовать интерфейс CFRunLoop.

    Также обратите внимание, что вы, вероятно, не должны запускать цикл цикла очень долго внутри блока, запущенного в очереди GCD, потому что вы связываете поток, который GCD ожидает контролировать. Если вам нужно запустить цикл выполнения в течение длительного времени, вы должны запустить свой собственный поток для него. Вы можете увидеть пример этого в функции _legacyStreamRunLoop в CFStream.c. Обратите внимание, как это делает выделенный цикл выполнения потока доступным в статической переменной с именем sLegacyRL, которую он инициализирует под защитой dispatch_semaphore_t.

Ответ 2

  • Связь между циклом запуска основного потока и основной диспетчерской очередью состоит только в том, что оба они запускаются в основном потоке и что блоки, отправленные в основную очередь, чередуются в основном потоке с событиями, обработанными на основном runloop.

    Как говорится в Concurrency Руководство по программированию:

    Основная очередь отправки - глобально доступная последовательная очередь, которая выполняет задачи в основном потоке приложений. Эта очередь работает с циклом запуска приложений (если таковой имеется) для чередования выполнения задач с очередью с выполнением других источников событий, связанных с циклом запуска. Поскольку он работает в основном потоке приложений, основная очередь часто используется в качестве ключевой точки синхронизации для приложения.

  • При отправке в фоновый поток он не создает NSRunLoop для этих рабочих потоков. Вам также не нужен цикл запуска для этих фоновых потоков. Нам приходилось создавать собственные NSRunLoop для фоновых потоков (например, при планировании NSURLConnection в фоновом потоке), но этот шаблон не требуется очень часто.

    Для вещей, которые исторически требуют циклов запуска, часто используются лучшие механизмы, если они выполняются в фоновом потоке. Например, вместо NSURLConnection вы теперь используете NSURLSession. Или вместо NSTimer на NSRunLoop в фоновом потоке вы создадите источник отправки таймера GCD.

  • Относительно того, кто "знает" поток, рабочий поток идентифицируется при отправке в очередь. Поток не является свойством очереди, а скорее назначен для очереди, когда очередь нуждается в ней.

  • Если вы хотите создать NSRunLoop для рабочего потока (который обычно не должен делать, так или иначе), вы создаете его и отслеживаете его самостоятельно. И при планировании потока с циклом запуска я был бы склонен создавать сам NSThread и планировать цикл цикла на нем, а не связывать один из рабочих потоков GCD.