2015-11-03 2 views
2

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

Так у меня есть 3 темы:

  1. A (основной).
  2. B (управляется GCD).
  3. C (вручную создан для обработки интенсивных операций сокета).

Сценарий следующий:

В applicationDidEnterBackground: обработчике (который, конечно, выполнен на thread A) затянувшаяся задача началась thread B завершить все текущие операции (сохранить состояние приложения, закройте разъем и т. д.). В этой задаче мне нужно подождать, пока сокет правильно завершит свою работу на thread C и только после этого продолжит эту долговременную задачу.

Ниже упрощен код:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 
    // Some synchronous task. 
    [stateManager saveState]; 

    // Here I need to wait until the socket finishes its task. 
    ... 

    // Continuing of the long-running task. 
    ... 
} 

Каков приемлемый способ для выполнения этой задачи. Все в порядке, если я что-то сделаю?

while (socket.isConnected) 
{ 
     [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; 
} 

// Continuing of the long-running task. 

Или, может быть что-то не так в моей текущей архитектуры, и мне нужно использовать NSOperation для сериализации асинхронных задач как-то, например?

обновление: Проблема была решена с помощью dispatch_semaphoreAPIs как предложено @Rob Napier.

+1

Возможная дубликация [Синхронизация/схема ожидания для передачи сигналов с несколькими потоками (Obj-C)?] (Http://stackoverflow.com/questions/4099801/synchronization-wait-design-for-cross-thread-event-signaling -obj-c) –

+0

Это одно из возможных решений, но @Rob Napier предложил еще один. –

ответ

1

Во-первых, не думайте об этом как о потоках, и если вы создаете нить с NSThread или performSelectorInBackground: (или, что еще хуже, pthreads), не делайте этого. Используйте GCD. GCD создает очереди. Блоки заказов очередей, которые в конечном итоге выполняются в потоках, но потоки являются деталями реализации, и не существует сопоставления очередей 1: 1 для потоков. См. Migrating Away from Threads для более подробного обсуждения этого вопроса.

На вопрос о том, как ждать какой-либо другой операции, возможно, вам нужен инструмент dispatch_semaphore. Вы создаете семафор и передаете его обеим операциям. Затем вы вызываете dispatch_semaphore_wait, когда хотите что-то подождать, и dispatch_sempahore_signal, когда вы хотите указать, что что-то произошло. См. Using Dispatch Semaphores to Regulate the Use of Finite Resources для более примера кода. «Конечный ресурс» в этом случае - «сокет». Вы хотите подождать, пока другая часть не будет выполнена, и вернет его в пул.

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

+0

Роб спасибо за ответ. Я знаю, что использование GCD, когда это возможно, является хорошей идеей, кроме того, что Apple настаивает на этом. Но в моем случае ручное управление потоками необходимо (по «NSThread»), потому что мне нужно создать 2 долгоживущих считываемых и перезаписываемых потока, подключенных к сокету TCP/IP, поэтому GCD в этом случае не подходит. Спасибо за 'dispatch_semaphore_ *' это было полезно! –

+1

Я не уверен, почему вы считаете, что NSThread необходим здесь. См. CocoaAsyncSocket, особенно его GCD impl: https://github.com/robbiehanson/CocoaAsyncSocket. Также https://github.com/robbiehanson/CocoaHTTPServer. Также см. Dispatch_io и NSStream. Существует множество способов, которыми вы можете (и должны) читать из сокетов без необходимости долговременного потока. –

+0

Спасибо, что открыли глаза на гнезда на базе GCD! Я был немного смущен устаревшим кодом текущего проекта. –

1

Я бы использовал NSOperation с зависимостями. Итак, у вас есть задачи А - основной поток - ака «точка входа» B - тяжелый мальчик работать в фоновом режиме C - что-то другое тяжелого бежать после того, как сокет закончил

  1. Вашего тяжелого задания от «B» is OperationB
  2. Предположим, что ваш сокет-каркас способен работать синхронно в текущем потоке? - то это ваш OperationSend
  3. ли остальное делать в фоновом режиме - OperationC

там у вас есть цепочка операций, в зависимости друг от друга:

OperationB -> OperationSend -> OperationC

+0

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

Смежные вопросы