Grand Central Dispatch — пример использования iOS

Давайте создадим пример того, что мы рассматривали в статье  Grand Central Dispatch в iOS на Swift. Очереди

Начнем с создания собственной очереди.

Для создания очереди нам требуется указать имя очереди, что как мы помним облегчит жизнь при отладке iOS приложения. Укажем тип очереди, serieal (по-умолчанию) или concurrent и тип очереди, определяемый QOS.

//
// proSwift.ru
//

let attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INITIATED, 0)
let primeQueue = dispatch_queue_create("ru.proSwift.primer", attr)

Имя очереди

Давайте создадим вспомогательную функцию для вывода в консоль имени текущей очереди.

//
// proSwift.ru
//

func currentQueueName() -> String? {
    let label = dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL)
    return String(CString: label, encoding: NSUTF8StringEncoding)
}

Думаю, тут нет ничего сложного. Единственное, что стоит хорошо понимать, что GCD это набор функций языка программирования C. Поэтому нужно сделать преобразование строки из типа CString в String.

Теперь давайте опробуем в деле нашу новую функцию.

//
// proSwift.ru
//

let currentQueueNameString = currentQueueName()
print(currentQueueNameString) // 1


dispatch_sync(primeQueue) {
    print(currentQueueName()) // 2
}

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

//
// proSwift.ru
//

Optional("com.apple.main-thread") // 1
Optional("ru.proSwift.primer")    // 2

Собственно, результат вполне предсказуем.

ASYNC vs SYNC

Стоит на примере разобраться чем отличаются функции dispatch_async и dispatch_sync. Для этого будем использовать нашу созданную выше очередь. Этот код мы будем использовать для того, чтобы увидеть начало и окончание передачи задач в очередь, а также момент окончания выполнения задачи.  Строки, которые попадают в консоль и комментарии говорят сами за себя. По коду ниже не должно возникнуть вопросов.

//
// proSwift.ru
//

// ASYNC
print("===  ASYNC:: Асинхронно посылаем в очередь primer ===")
dispatch_async(primeQueue) {
    print(" - - - async:: Выполняем в очереди \(currentQueueName()!)")
}
print("=== ASYNC:: Закончили асинхронно передавать задачу в очередь primer ===")



// SYNC
print("=== SYNC:: Синхронно посылаем в очередь primer ===")
dispatch_sync(primeQueue) {
    print(" - - - sync:: Выполняем в очереди \(currentQueueName()!)")
}
print("=== SYNC:: Закончили синхронно передавать задачу в очередь primer ===\n")

Внимательно посмотрите на результат работы этой части программы:

=== ASYNC:: Асинхронно посылаем в очередь primer ===
=== ASYNC:: Закончили асинхронно передавать задачу в очередь primer ===
 - - - async:: Выполняем в очереди ru.proSwift.primer

=== SYNC:: Синхронно посылаем в очередь primer ===
 - - - sync:: Выполняем в очереди ru.proSwift.primer
=== SYNC:: Закончили синхронно передавать задачу в очередь primer ===

Хорошо видно, что в  блоке ASYNC консоль отобразила сообщение о начале передачи задачи, об окончании передачи, и лишь потом появилось сообщение о выполнении задачи.

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

SERIAL vs CONCURRENT

Для демонстрации работы разного типа очередей создадим функцию hardJob(), в которой будем останавливать поток на одну секунду с помощью функции sleep(1), и после паузы выводить сообщение об окончании работы. Для удобства я также добавил в лог имя очереди и время, которое было затрачено на выполнение задачи. Вывод времени реализуется с помощью функций startClock() — которая запоминает момент времени «сейчас» и showClock(), которая выводит время, прошедшее с момента выполнения  startClock().

В примере выше мы создали очередь типа Serial, поэтому ее мы опробуем первой. Пошлем четыре раза задачу hardJob() в нашу очередь.

Затем в блоке //CONCURRENT мы создадим очередь Concurrent и также пошлем задачу hardJob() во вновь созданную очередь. У меня получилось, что я послал ее пять раз. Но для нашего примера это не принципиально.

//
// proSwift.ru
//

func hardJob() {
    sleep(1)
    print("\(showClock()) :: \(currentQueueName()!) :: Закончили!")
}

// SERIAL
// наша primeQueue типа Serial, поэтому попробуем функционал именно этой очереди
startClock() //Обнуление таймера
print("=== Начало выполнения Serial - очереди ===")

dispatch_async(primeQueue, hardJob)
dispatch_async(primeQueue, hardJob)
dispatch_async(primeQueue, hardJob)
dispatch_async(primeQueue, hardJob)

print(" - = - = - = - = - = - = - = - = - = - = - = - = - = - ")


sleep(5) // перерыв пять секунд между работой с разными типами очередей для удобства восприятия лога вывода в консоль.


// CONCURRENT
let concurrentQueue = dispatch_queue_create("ru.proSwift.concurrent", DISPATCH_QUEUE_CONCURRENT)

startClock() //Обнуление таймера
print("=== Начало выполнения concurrent - очереди ===")

dispatch_async(concurrentQueue, hardJob)
dispatch_async(concurrentQueue, hardJob)
dispatch_async(concurrentQueue, hardJob)
dispatch_async(concurrentQueue, hardJob)
dispatch_async(concurrentQueue, hardJob)

print(" - = - = - = - = - = - = - = - = - = - = - = - = - = - ")

Результат работы последней части программы:

=== Начало выполнения Serial - очереди ===
 - = - = - = - = - = - = - = - = - = - = - = - = - = - 
1.00458300113678 :: ru.proSwift.primer :: Закончили!
2.00884199142456 :: ru.proSwift.primer :: Закончили!
3.01151996850967 :: ru.proSwift.primer :: Закончили!
4.01502102613449 :: ru.proSwift.primer :: Закончили!

=== Начало выполнения concurrent - очереди ===
 - = - = - = - = - = - = - = - = - = - = - = - = - = - 
1.00319999456406 :: ru.proSwift.concurrent :: Закончили!
1.00575304031372 :: ru.proSwift.concurrent :: Закончили!
1.00585901737213 :: ru.proSwift.concurrent :: Закончили!
1.00587600469589 :: ru.proSwift.concurrent :: Закончили!
1.00663203001022 :: ru.proSwift.concurrent :: Закончили!

Итак, видно, что все задачи в очереди ru.proSwift.primer выполняются последовательно, и на выполнение всех четырех потребовалось 4 секунды. А в очереди  ru.proSwift.concurrent задачи выполнились за секунду, даже с учетом того, что их пять.

И еще момент, внимательный читатель заметит, что строка кода, которая выводит в консоль линию отреза …

print(" - = - = - = - = - = - = - = - = - = - = - = - = - = - ")

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

Оставляю читателям возможность перечитать эту статью еще раз и поразмышлять об этом в комментариях…

Метки: ,
4 комментария на “Grand Central Dispatch — пример использования iOS
  1. Анатолий:

    Добрый день.
    Пытался организовать синхронную загрузку данных с помощью Alamofire и не получилось.
    Вы не могли бы показать, как это можно сделать?
    Как я понял, основная сложность заключается в том, что в файле Download.swift фреймворка организованы свои механизмы асинхронной загрузки. И как их обойти большой вопрос.
    Мне бы хотелось, чтобы пока грузятся данных из инета отображался прогрессбар и никакие действия в основном интерфейсе не были доступны (т.е. чтобы я, например, не мог перейти на другой вью при нажатии на одном из item’во в таб баре).
    p.s. Если нужен пример того, что я написал, то могу скинуть.

  2. Анатолий:

    Спасибо!!!
    Сейчас посмотрю.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

*

Этот сайт использует Akismet для борьбы со спамом. Узнайте как обрабатываются ваши данные комментариев.