Каковы разные способы вызова моего метода в отдельном потоке? - PullRequest
31 голосов
/ 28 ноября 2011

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

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

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

b) с использованием простого ярлыка (доступно с 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 для более подробной информации, потому что это только мои мысли.

Ответы [ 2 ]

25 голосов
/ 28 ноября 2011

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

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

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

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

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

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

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

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

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

EDIT

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

РЕДАКТИРОВАТЬ 2

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

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

Ссылка

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

Если вы еще не прочитали его, вам следует взглянуть.

0 голосов
/ 03 января 2018

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

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

...