Разъяснение по потокам и циклам запуска В Cocoa

Я пытаюсь узнать про потоки, и я полностью смущен. Я уверен, что все ответы есть в яблочных документах, но я просто нашел, что это очень сложно для разбивки и переваривания. Может быть, кто-то может убрать что-то или 2 для меня.

1) performSelectorOnMainThread

Описанное выше просто регистрирует событие в цикле основного запуска или это как-то новый поток, хотя метод говорит "mainThread"? Если цель потоков - облегчить обработку основного потока, как это помогает?

2) RunLoops

Правда ли, что если я хочу создать полностью отдельный поток, я использую   "DetachNewThreadSelector"? Начинает ли вызов инициировать цикл запуска по умолчанию для потока, который был создан? Если да, то где туда заходят циклы запуска?

3) И, наконец, я видел примеры с использованием NSOperationQueue. Правда ли, что если вы используете performSelectorOnMainThread, потоки все равно находятся в очереди, поэтому NSOperation не требуется?

4) Должен ли я забыть обо всем этом и просто использовать Grand Central Dispatch вместо этого?

Ответ 1

Запустить петли

Вы можете думать, что Run Loop является обработкой событий для цикла, связанного с потоком. Это обеспечивается системой для каждого потока, но она запускается автоматически только для основного потока.

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

[NSRunLoop currentRunLoop]

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

PerformSelector

performSelectorOnMainThread: добавляет цель и селектор в специальный источник входного сигнала, называемый источником входного сигнала. Цикл запуска основного потока удаляет этот входной источник и обрабатывает вызов метода один за другим, как часть цикла обработки событий.

NSOperation/NSOperationQueue

Я думаю о NSOperation как о способе явного объявления различных задач внутри приложения, которое занимает некоторое время, но может выполняться в основном независимо. Это проще в использовании, чем самостоятельно отсоединить новую ветку и самостоятельно поддерживать различные вещи. Основной NSOperationQueue автоматически поддерживает набор фоновых потоков, которые он использует повторно, и запускайте NSOperations параллельно. Так что да, если вам просто нужно поставить в очередь операции в основном потоке, вы можете покончить с NSOperationQueue и просто использовать performSelectorOnMainThread:, но это не главная точка NSOperation.

НОД

GCD - это новая инфраструктура, внедренная в Snow Leopard. NSOperationQueue теперь выполняется поверх него. Он работает на уровне функций/блоков. Подача блоков на dispatch_async чрезвычайно удобна, но для большей части операций я предпочитаю использовать NSOperation, особенно если этот фрагмент используется из разных мест в приложении.

Резюме

Вам нужно прочитать Официальный Apple Doc! В этом вопросе есть много информационных сообщений в блоге.

Ответ 2

1) performSelectorOnMainThread

Описанное выше просто регистрирует событие в цикле основного запуска...

Вы спрашиваете о деталях реализации. Не беспокойтесь о том, как это работает.

Что он делает, это выполнить селектор в основном потоке.

... или это как-то новый поток, хотя метод говорит "mainThread"?

Нет.

Если цель потоков - облегчить обработку основного потока, как это помогает?

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

Существуют и другие методы для создания новых вторичных потоков, хотя NSOperationQueue и GCD - это, как правило, более простые способы сделать это.

2) RunLoops

Правда ли, что если я хочу создать совершенно отдельный поток, я использую "detachNewThreadSelector"?

Это не имеет ничего общего с циклами запуска.

Да, это один из способов начать новый поток.

Запускает ли запуск этого запуска цикл запуска по умолчанию для созданного потока?

Нет.

Я не знаю, что вы "вызываете start on" здесь, во всяком случае. detachNewThreadSelector: ничего не возвращает, и он немедленно начинает поток. Я думаю, вы смешали это с NSOperations (который вы также не начинаете сами) - это работа в очереди).

Если это так, когда в него входят циклы запуска?

Запуск циклов только существует, по одному на поток. На стороне реализации они, вероятно, лениво создаются по требованию.

3) И, наконец, я видел примеры с использованием NSOperationQueue. Правда ли, что если вы используете performSelectorOnMainThread, потоки все равно находятся в очереди, поэтому NSOperation не требуется?

Эти две вещи не связаны.

performSelectorOnMainThread: выполняет именно это: выполняет селектор в основном потоке.

NSOperations выполняется на вторичных потоках, по одному на операцию.

Операционная очередь определяет порядок, в котором запускаются операции (и их потоки).

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

4) Должен ли я забыть обо всем этом и просто использовать Grand Central Dispatch вместо этого?

GCD - это более или менее тот же набор концепций, что и операционные очереди. Вы не поймете этого, если не понимаете другого.


Итак, для чего все это полезно?

Циклы выполнения

В потоке есть способ запланировать все. Некоторые могут быть запланированы в определенную дату (таймеры), другие - просто "всякий раз, когда вы обходите" (источники). Большинство из них имеют нулевую стоимость при простоях, потребляя только процессорное время, когда происходит событие (срабатывает таймер или источник сигнала), что заставляет циклы запуска очень эффективным способом одновременного выполнения нескольких действий без каких-либо потоков.

Обычно вы не обрабатываете цикл выполнения при создании запланированного таймера; таймер добавляет себя к циклу запуска для вас.

Темы

Потоки позволяют одновременно совершать несколько действий на разных процессорах. Вещь 1 может произойти в потоке A (на процессоре 1), а вещь 2 - на потоке B (на процессоре 0).

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

NSOperationQueue и GCD

У вас есть то, что вам нужно. Это операция. Вы не можете сделать это в основном потоке, или просто отправить сообщение, как обычно; вам нужно запустить его в фоновом режиме, на вторичном потоке.

Чтобы достичь этого, выражайте его как объект NSOperation (вы создаете подкласс NSOperation и создаете его экземпляр) или block (или оба), затем добавьте его либо в NSOperationQueue (NSOperations, включая NSBlockOperation), либо в очередь отправки (пустой блок).

GCD может использоваться для того, чтобы все происходило и на основной теме; вы можете создавать последовательные очереди и добавлять к ним блоки. Последовательная очередь, как следует из ее названия, будет работать ровно по одному блоку за раз, а не параллельно с ними.

Итак, что мне делать?

Я бы не рекомендовал напрямую создавать потоки. Вместо этого используйте NSOperationQueue или GCD; они заставляют вас улучшать привычки мышления, которые уменьшат риск вашего потокового кода, вызывающего головные боли.

Для вещей, которые периодически запускаются, не вписываясь в модель NSOperations и GCD, которую я должен сделать, рассмотрим только использование цикла запуска в основном потоке. Скорее всего, вам не нужно ставить его на поток в конце концов. Например, цикл рендеринга в 3D-игре может быть простым таймером.