2017-02-09 4 views
4

(Возможно, ответил How does a serial dispatch queue guarantee resource protection?, но я не понимаю, как)Как очередь очередей очереди/частной диспетчеризации знает, когда задача завершена?

Вопрос

Как знать НОД, когда асинхронная задача (например, сетевая задача) завершена? Должен ли я использовать dispatch_retain и dispatch_release для этой цели? Обновление: Я не могу назвать любой из этих методов с помощью ARC ... Что делать?

Подробности

Я взаимодействующий с 3-сторонней библиотеки, которая делает много доступа к сети. Я создал оболочку через небольшой класс, который в основном предлагает все методы, которые мне нужны из стороннего класса, но обертывает вызовы в dispatch_async(serialQueue) {() -> Void in (где serialQueue является членом моего класса-оболочки).

Я стараюсь, чтобы каждый вызов базовой библиотеки заканчивался до начала следующего (так или иначе, что еще не реализовано в библиотеке).

ответ

7

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

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

Например, для следующего кода:

dispatch_async(serialQueue) { 
    print("Start") 
    dispatch_async(backgroundQueue) { 
     functionThatTakes10Seconds() 
     print("10 seconds later") 
    } 
    print("Done 1st") 
} 

dispatch_async(serialQueue) { 
    print("Start") 
    dispatch_async(backgroundQueue) { 
     functionThatTakes10Seconds() 
     print("10 seconds later") 
    } 
    print("Done 2nd") 
} 

Выходной сигнал будет что-то вроде:

Start

Совершено первое

Начало

Совершено второй

через 10 секунд

через 10 секунд

Обратите внимание, что первые 10 секунд задача не завершена до второго последовательного задания отправляется. Теперь сравните:

dispatch_async(serialQueue) { 
    print("Start") 
    dispatch_sync(backgroundQueue) { 
     functionThatTakes10Seconds() 
     print("10 seconds later") 
    } 
    print("Done 1st") 
} 

dispatch_async(serialQueue) { 
    print("Start") 
    dispatch_sync(backgroundQueue) { 
     functionThatTakes10Seconds() 
     print("10 seconds later") 
    } 
    print("Done 2nd") 
} 

Выходной сигнал будет что-то вроде:

Start

10 секунд

Совершено первое

Start

через 10 секунд

Done второго

Обратите внимание, что на этот раз, потому что 10 второй задача была послана синхронно серийных очереди были заблокированы, и вторая задача не началась, пока первый не закончил.

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

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

dispatch_async(serialQueue) { 
    let dg = dispatch_group_create() 
    dispatch_group_enter(dg) 
    print("Start") 
    dispatch_async(backgroundQueue) { 
     functionThatTakes10Seconds() 
     print("10 seconds later") 
     dispatch_group_leave(dg) 
    } 
    dispatch_group_wait(dg) 
    print("Done") 
} 

Это выведет

Старт

через 10 секунд

Совершено

dg.wait() блокирует серийный очереди, пока число dg.leave вызовов не совпадает с номером из dg.enter звонки. Если вы используете эту технику, вам нужно быть осторожным, чтобы все возможные пути завершения для вашей завернутой операции вызывали dg.leave. Есть также варианты на dg.wait(), которые принимают параметр тайм-аута.

+0

это FAN-TASTIC! вопрос: если DispatchGroup является нерешенным идентификатором, значит ли это, что я на Swift 2? 1? (Я знаю, что я не на 3) Должен ли я использовать dispatch_group_create, dispatch_group_enter и dispatch_group_leave как в (например) http://commandshift.co.uk/blog/2014/03/19/using-dispatch-groups-to -wait-for-multiple-web-services /? – Michael

+1

Правильно; Я по умолчанию Swift 3 :) Я должен, вероятно, очистить свой ответ, так как на данный момент это смесь из 2 и 3. – Paulw11

+1

, вы думаете, что dispatch_group_wait необходимо? например, если бы у меня было 2 блока, как у вас в самом конце вашего ответа, не могли ли вызовы dispatch_group_enter и dispatch_group_leave реализовать сериализацию? (а выход будет таким же, как и у вашего второго блока?) – Michael

0

Ответ на этот вопрос в своих вопросах тела:

Я пытаюсь убедиться, что каждый вызов нижележащих библиотеки заканчивается до следующего начинается

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


Я не очень понимаю, вопроса в названии, хотя:

Как делает последовательные очереди ... знает, когда задача будет завершена?

1

Как уже упоминалось, DispatchGroup - очень хороший механизм для этого.

Вы можете использовать его для синхронных задач:

let group = DispatchGroup() 
DispatchQueue.global().async(group: group) { 
    syncTask() 
} 

group.notify(queue: .main) { 
    // done 
} 

Лучше использовать notify чем wait, поскольку wait действительно блокирует текущий поток, так что это безопасно на неосновных потоков.

Вы также можете использовать его для выполнения асинхронных задач:

let group = DispatchGroup() 
group.enter() 
asyncTask { 
    group.leave() 
} 

group.notify(queue: .main) { 
    // done 
} 

Или вы даже можете выполнить любое количество параллельных задач любого синхронности:

let group = DispatchGroup() 

group.enter() 
asyncTask1 { 
    group.leave() 
} 

group.enter() //other way of doing a task with synchronous API 
DispatchQueue.global().async { 
    syncTask1() 
    group.leave() 
} 

group.enter() 
asyncTask2 { 
    group.leave() 
} 

DispatchQueue.global().async(group: group) { 
    syncTask2() 
} 

group.notify(queue: .main) { 
    // runs when all tasks are done 
} 

Важно отметить несколько вещей ,

  1. Всегда проверяйте, если ваши асинхронные функции вызовите завершения обратного вызова, иногда сторонние библиотеки забывают о том, что, или случаи, когда ваш self является weak и никто не удосужился проверить, если тело получил оценены, когда self является nil. Если вы не проверите это, вы можете повесить и никогда не получить обратный вызов уведомления.
  2. Не забудьте выполнить все необходимые group.enter() и group.async(group: group) звонки, прежде чем звонить по телефону group.notify. В противном случае вы можете получить условие гонки, и блок group.notify может выстрелить, прежде чем вы действительно закончите свои задачи.

BAD Пример

let group = DispatchGroup() 

DispatchQueue.global().async { 
    group.enter() 
    syncTask1() 
    group.leave() 
} 

group.notify(queue: .main) { 
    // Can run before syncTask1 completes - DON'T DO THIS 
} 

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

  • Нет связанных вопросов^_^