iOS
GCD (Grand Central Dispatch)
Szukaj…
Wprowadzenie
Grand Central Dispatch (GCD) to odpowiedź Apple na wielowątkowość. Jest to lekka platforma do wykonywania zadań synchronicznie lub asynchronicznie w kolejkach i obsługuje wątki procesora za kulisami.
Temat pokrewny: Współbieżność
Utwórz kolejkę wysyłki
Możesz utworzyć własną kolejkę za pomocą dispatch_queue_create
Cel C
dispatch_queue_t queue = dispatch_queue_create("com.example.myqueue", DISPATCH_QUEUE_SERIAL);
Szybki
// 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
Uzyskiwanie głównej kolejki
Główna kolejka to kolejka wysyłkowa, w której odbywają się wszystkie aktualizacje interfejsu użytkownika i umieszczany jest kod dotyczący zmian interfejsu użytkownika.
Musisz przejść do głównej kolejki, aby zaktualizować interfejs użytkownika po zakończeniu procesu asynchronicznego, takiego jak NSURLSession
Istnieją dwa rodzaje wywołań kolejki głównej synchronous
i asynchronous
. Gdy wywołujesz coś synchronously
, oznacza to, że wątek, który zainicjował tę operację, czeka na zakończenie zadania przed kontynuowaniem. Asynchronous
oznacza, że nie będzie czekać.
Kod Cel-C
Synchronous
połączenie z kolejką główną
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
Asynchronous
wywołanie kolejki głównej
dispatch_async(dispatch_get_main_queue(), ^{
// do work here to Usually to update the User Interface
});
SWIFT 3
Asynchronous
wywołanie kolejki głównej
DispatchQueue.main.async {
}
Synchronous
połączenie z kolejką główną
DispatchQueue.main.sync {
}
Grupa wysyłkowa
DispatchGroup pozwala na agregację synchronizacji pracy. Możesz ich użyć, aby przesłać wiele różnych elementów pracy i śledzić ich ukończenie, nawet jeśli mogą działać w różnych kolejkach. To zachowanie może być pomocne, gdy nie można dokonać postępu, dopóki wszystkie określone zadania nie zostaną ukończone.
Scenariusz, w którym może to być przydatne, to jeśli masz wiele wywołań usługi internetowej, które muszą zakończyć się przed kontynuowaniem. Na przykład musisz pobrać wiele zestawów danych, które muszą zostać przetworzone przez jakąś funkcję. Musisz poczekać na zakończenie wszystkich usług sieciowych przed wywołaniem funkcji w celu przetworzenia wszystkich odebranych danych.
Szybki 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!")
}
Alternatywnie, jeśli nie chcesz czekać na zakończenie grup, ale zamiast tego chcesz uruchomić funkcję po zakończeniu wszystkich zadań, użyj funkcji notify
zamiast 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
}
Przykładowe dane wyjściowe:
starting long running tasks
long task 0 done!
long task 3 done!
long task 1 done!
long task 2 done!
all tasks done!
Aby uzyskać więcej informacji, zapoznaj się z Dokumentami Apple lub pokrewnym tematem
Wyślij semafor
DispatchSemaphore zapewnia wydajną implementację tradycyjnego semafora zliczającego, którego można użyć do kontrolowania dostępu do zasobu w wielu kontekstach wykonywania.
Scenariusz, w którym należy użyć semafora, może być taki, gdy wykonujesz odczyt / zapis pliku, jeśli wiele zadań próbuje jednocześnie czytać i zapisywać z pliku, może to zwiększyć wydajność, aby każde zadanie czekało na swoją kolej, tak aby aby nie przeciążać kontrolera I / O.
Szybki 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
}
}
}
Przykładowy wynik: (zauważ znaczniki czasu)
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
Aby uzyskać więcej informacji, zapoznaj się z Dokumentami Apple
Szeregowe kontra równoległe kolejki wysyłkowe
Szybki 3
Kolejka szeregowa
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)")
}
}
}
Przykładowe dane wyjściowe:
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
Współbieżna kolejka
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)")
}
}
}
Przykładowe dane wyjściowe:
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
Dyskusja
Jak widać z powyższych przykładów, kolejka szeregowa wykona każde zadanie w kolejności, w jakiej są przesyłane do kolejki. Każde zadanie będzie czekać na zakończenie poprzedniego zadania przed jego wykonaniem. Jeśli chodzi o współbieżną kolejkę, każde zadanie nie czeka na inne w kolejce i wykonuje się tak szybko, jak to możliwe; zaletą jest to, że wszystkie zadania w kolejce będą uruchamiane jednocześnie w osobnych wątkach, co sprawia, że kolejka współbieżna zajmuje mniej czasu niż kolejka szeregowa.
Jeśli kolejność wykonywania zadań nie jest ważna, zawsze używaj współbieżnej kolejki dla uzyskania najlepszej wydajności.