iOS
GCD (Grand Central Dispatch)
Поиск…
Вступление
Grand Central Dispatch (GCD) - ответ Apple на многопоточность. Это облегченная структура для выполнения задач синхронно или асинхронно в очередях и обрабатывает потоки ЦП для вас за кулисами.
Связанная тема: параллелизм
Создание очереди отправки
Вы можете создать свою собственную очередь, используя dispatch_queue_create
Objective-C
dispatch_queue_t queue = dispatch_queue_create("com.example.myqueue", DISPATCH_QUEUE_SERIAL);
стриж
// Before Swift 3
let queue = dispatch_queue_create("com.example.myqueue", DISPATCH_QUEUE_SERIAL)
// Swift 3
let queue = DispatchQueue(label: "com.example.myqueue") //default is serial queue, unless .concurrent is specified as an attribute otherwise
Получение главной очереди
Главной очередью является очередь отправки, в которой все обновления пользовательского интерфейса, и код с изменениями пользовательского интерфейса.
Вам нужно попасть в основную очередь, чтобы обновить интерфейс после завершения асинхронного процесса, такого как NSURLSession
Существует два типа вызовов основной очереди: synchronous
и asynchronous
. Когда вы вызываете что-то synchronously
, это означает, что поток, инициировавший эту операцию, будет ждать завершения задачи перед продолжением. Asynchronous
означает, что он не будет ждать.
Цель кода-C
Synchronous
вызов главной очереди
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
Asynchronous
вызов главной очереди
dispatch_async(dispatch_get_main_queue(), ^{
// do work here to Usually to update the User Interface
});
SWIFT 3
Asynchronous
вызов главной очереди
DispatchQueue.main.async {
}
Synchronous
вызов главной очереди
DispatchQueue.main.sync {
}
Диспетчерская группа
DispatchGroup позволяет выполнять агрегатную синхронизацию работы. Вы можете использовать их для отправки нескольких разных рабочих элементов и отслеживания, когда они все завершатся, даже если они могут выполняться в разных очередях. Такое поведение может быть полезно, когда прогресс не может быть достигнут, пока все указанные задачи не будут завершены.
Сценарий, когда это может быть полезно, - это если у вас несколько вызовов webservice, которые все нужно завершить, прежде чем продолжить. Например, вам нужно загрузить несколько наборов данных, которые необходимо обработать с помощью некоторых функций. Вы должны дождаться завершения всех веб-сервисов перед вызовом функции для обработки всех полученных данных.
Swift 3
func doLongTasksAndWait () {
print("starting long running tasks")
let group = DispatchGroup() //create a group for a bunch of tasks we are about to do
for i in 0...3 { //launch a bunch of tasks (eg a bunch of webservice calls that all need to be finished before proceeding to the next ViewController)
group.enter() //let the group know that something is being added
DispatchQueue.global().async { //run tasks on a background thread
sleep(arc4random() % 4) //do some long task eg webservice or database lookup (here we are just sleeping for a random amount of time for demonstration purposes)
print("long task \(i) done!")
group.leave() //let group know that the task is finished
}
}
group.wait() //will block whatever thread we are on here until all the above tasks have finished (so maybe dont use this function on your main thread)
print("all tasks done!")
}
С другой стороны , если вы не хотите ждать , пока группы , чтобы закончить, но вместо этого хочет , чтобы запустить функцию , когда все задачи выполнили, используйте notify
функцию вместо group.wait()
group.notify(queue: DispatchQueue.main) { //the queue: parameter is which queue this block will run on, if you need to do UI updates, use the main queue
print("all tasks done!") //this will execute when all tasks have left the group
}
Пример вывода:
starting long running tasks
long task 0 done!
long task 3 done!
long task 1 done!
long task 2 done!
all tasks done!
Для получения дополнительной информации см. Документы Apple или соответствующую тему
Диспетчерский семафор
DispatchSemaphore обеспечивает эффективную реализацию традиционного счетного семафора, который может использоваться для управления доступом к ресурсу в нескольких контекстах выполнения.
Сценарий, когда использовать семафор, может быть, если вы выполняете чтение / запись файлов, если несколько задач пытаются одновременно читать и писать из файла, это может увеличить вашу производительность, чтобы каждая задача дождалась своей очереди так, чтобы чтобы не перегружать контроллер ввода-вывода.
Swift 3
func do2TasksAtATime () {
print("starting long running tasks (2 at a time)")
let sem = DispatchSemaphore(value: 2) //this semaphore only allows 2 tasks to run at the same time (the resource count)
for i in 0...7 { //launch a bunch of tasks
DispatchQueue.global().async { //run tasks on a background thread
sem.wait() //wait here if no resources available
sleep(2) //do some long task eg file access (here we are just sleeping for a 2 seconds for demonstration purposes)
print("long task \(i) done! \(Date())")
sem.signal() //let the semaphore know this resource is now available
}
}
}
Пример вывода: (обратите внимание на отметки времени)
starting long running tasks (2 at a time)
long task 0 done! 2017-02-16 07:11:53 +0000
long task 1 done! 2017-02-16 07:11:53 +0000
long task 2 done! 2017-02-16 07:11:55 +0000
long task 3 done! 2017-02-16 07:11:55 +0000
long task 5 done! 2017-02-16 07:11:57 +0000
long task 4 done! 2017-02-16 07:11:57 +0000
long task 6 done! 2017-02-16 07:11:59 +0000
long task 7 done! 2017-02-16 07:11:59 +0000
Для получения дополнительной информации см. Документы Apple
Серийные и параллельные диспетчерские очереди
Swift 3
Серийная очередь
func serialQueues () {
let serialQueue = DispatchQueue(label: "com.example.serial") //default queue type is a serial queue
let start = Date ()
for i in 0...3 { //launch a bunch of tasks
serialQueue.async { //run tasks on a background thread, using our serial queue
sleep(2) //do some long task eg webservice or database lookup
let timeTaken = Date().timeIntervalSince(start)
print("serial long task \(i) done! total time taken: \(timeTaken)")
}
}
}
Пример вывода:
serial long task 0 done! total time taken: 2.07241100072861
serial long task 1 done! total time taken: 4.16347700357437
serial long task 2 done! total time taken: 6.23209798336029
serial long task 3 done! total time taken: 8.30682599544525
Параллельная очередь
func concurrentQueues () {
let concurrentQueue = DispatchQueue(label: "com.example.concurrent", attributes: .concurrent) //explicitly specify the queue to be a concurrent queue
let start = Date ()
for i in 0...3 { //launch a bunch of tasks
concurrentQueue.async { //run tasks on a background thread, using our concurrent queue
sleep(2) //do some long task eg webservice or database lookup
let timeTaken = Date().timeIntervalSince(start)
print("concurrent long task \(i) done! total time taken: \(timeTaken)")
}
}
}
Пример вывода:
concurrent long task 3 done! total time taken: 2.07092100381851
concurrent long task 0 done! total time taken: 2.07087397575378
concurrent long task 2 done! total time taken: 2.07086700201035
concurrent long task 1 done! total time taken: 2.07089096307755
обсуждение
Как видно из приведенных выше примеров, последовательная очередь будет выполнять каждую задачу в том порядке, в котором они будут отправлены в очередь. Каждая задача будет ждать завершения предыдущей задачи перед выполнением. Что касается параллельной очереди, каждая задача не ждет остальных в очереди и выполняется как можно скорее; преимущество состоит в том, что все задачи в очереди будут выполняться одновременно с отдельными потоками, в результате чего одновременная очередь занимает меньше времени, чем последовательная очередь.
Если порядок выполнения задач не важен, всегда используйте параллельную очередь для обеспечения максимальной эффективности.