it-swarm.com.ru

Каковы разные способы вызова моего метода в отдельном потоке?

У меня есть какой-то метод расчета данных (пусть это будет «myMethod:»), и я хочу переместить вызов в другой поток, потому что я не хочу блокировать основные функции пользовательского интерфейса. Итак, начал делать некоторые исследования о том, как вызвать мой метод в другом потоке. Насколько я вижу, в настоящее время есть много разных способов сделать это. Вот список:

а) с использованием чистых потоков (доступно с iOS 2.0): 

[NSThread detachNewThreadSelector:@selector(myMethod:) toTarget:self withObject:_myParamsArray];

б) с помощью простого ярлыка (доступно с iOS 2.0). Доступно из унаследованного NSObject, но метод также принадлежит классу NSThread:

[self performSelectorInBackground:@selector(myMethod:) withObject:_myParamsArray];

c) использование нового подхода к очередям Grand Central Dispatch (доступно с iOS 4.0):

dispatch_async(dispatch_get_global_queue(0, 0),
  ^ {
      [self myMethod:_myParamsArray];
    });

d) каким-то образом, используя некоторые классы, такие как NSOperation, NSBlockOperation или NSOperationQueue, хотя и не уверен, как именно это сделать (некоторый пример приветствуется) 

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

ОБНОВЛЕНИЕ: e) также найден другой способ выполнения подобных вещей с потоками - Выполнить циклы . Вот выдержка из документов Apple:

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

ИМХО, более или менее вы имеете дело с одной и той же задачей - как вызвать ваш метод в отдельном потоке для его асинхронной операции. 

ОБНОВЛЕНИЕ 2: Уже был некоторый опыт работы с NSInvocationOperation и NSOperationQueue, и ИМХО, это довольно удобно. Согласно документам Apple, GCD и NSOperations являются предпочтительным способом реализации многопоточности. А также, NSOperations работает на GCD, начиная с iOS 4.0. Короче говоря, вы создаете экземпляр NSIvocationOperation (как вызов вашего метода), затем создаете экземпляр NSOperationQueue и добавляете вызов в очередь. NSOperationQueue достаточно умен, вы можете создать несколько объектов NSIvocationOperation (обернуть ваши вызовы методов) и их в NSOperationQueue. В остальном уверен. NSOperationQueue определяет, сколько параллельных потоков необходимо для выполнения вызовов (NSInvocationOperation), и обрабатывает его для вас. Он может выполнить первый вызов в потоке A, затем второй в потоке B, третий в потоке C и далее в потоке B, так что вам не нужно об этом беспокоиться. Но если вы хотите, вы можете сказать, как максимальные потоки NSOperationQueue может использовать для выполнения вызовов (например, 1), но мне это не нужно. По умолчанию все задачи выполняются не в главном потоке, поэтому очереди операций по умолчанию асинхронны. Кроме того, если вы хотите выполнять вызовы методов (каждый из которых заключен в отдельный NSInvocationOperation) в строгой очереди, вы можете добавить зависимости и, таким образом, NSOperationQueue сохранит порядок вызова метода. Вот пример:

// wrap your method call into NSInvocationOperation object
NSInvocationOperation *currentOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(yourMethodCall) object:nil];

// _sharedOperationQueue is a shared NSOperationQueue 
// get all executing operations from the queue and get the last operation
_lastOperation = [[_sharedOperationQueue operations] lastObject];

// check if _lastOperation is not nil
if (_lastOperation) {

    // if not then add dependency, so the calls would be performed in a queue
    [currentOperation addDependency:_lastOperation];
}

// say - execute my method (operation)
[_sharedOperationQueue addOperation:currentOperation];

_lastOperation = currentOperation; // mark as last operation for adding dependency to the next operation

// the queue will retain invocation operation so you will release
[currentOperation release];

 ..... you can create another NSInvocationOperation and add it to the queue....

Что касается RUNLOOP, тем не менее иногда вы сталкиваетесь с ними, например, при запуске/планировании таймера или при установлении соединений NSURL. ИМХО, цикл выполнения можно сравнить с очередью задач, выполняемых в одном потоке. IMHO, runloop - это указатель на поток, который работает как очередь: у него есть задачи, которые могут генерировать события, и они будут помещены в конец очереди в этом потоке. По умолчанию все задачи в вашем приложении выполняются в одном цикле выполнения - в одном потоке. Я говорю, что это указатель, потому что, когда ваше приложение генерирует события, тогда приложение должно знать, куда поместить это событие (сенсорное событие или обратный вызов другого делегата) для выполнения. Конечно, вы должны прочитать о runloops для более подробной информации, потому что это только мои мысли.

30
Centurion

Обычно вы предпочитаете подход GCD.

Это проще, когда речь идет о синхронизации/блокировке, чем чистые потоки (NSThread - pthread), и это может быть более точным с точки зрения производительности.

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

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

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

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

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

Поэтому, если вам ДЕЙСТВИТЕЛЬНО нужно, чтобы отдельная задача выполнялась немедленно, используйте явные потоки. В противном случае используйте GCD.

Надеюсь, что это поможет вам : )

ПРАВКА

Примечание о performSelectorInBackground: он просто создает новый поток. Так что в принципе нет разницы с подходом NSThread.

Правка 2

NSOperation связанные вещи немного по-другому. В Mac OS X они реализованы с использованием GCD начиная с версии 10.6. Предыдущие версии используют темы.

На iOS они реализованы только с использованием потоков.

Ссылка

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

Если вы еще не прочитали это, вы должны посмотреть.

25
Macmade

Вот обновленный ответ на этот старый вопрос. Операции NSO теперь реализованы с помощью libdispatch для mac и ios. Они были в течение некоторого времени. Предпочтительным способом вызова другого потока является один из libdispatch или NSOperations. Основная идея, которую нужно иметь в виду, это «Качество обслуживания» или «QOS». QOS - это что-то, назначенное потоку, и когда вы назначаете его в очередь отправки и NSOperationQueue или NSOperation, вы, по сути, говорите: «Я хочу, чтобы это выполнялось в потоке с назначенным QOS». Особый случай - основная нить.

Могут быть случаи использования NSThread, но у меня их давно не было.

0
drkibitz