Swift Language
Konkurencja
Szukaj…
Składnia
Swift 3.0
DispatchQueue.main // Pobierz kolejkę główną
DispatchQueue (etykieta: „moja kolejka szeregowa”, atrybuty: [.serial, .qosBackground]) // Utwórz własną prywatną kolejkę szeregową
DispatchQueue.global (atrybuty: [.qosDefault]) // Uzyskaj dostęp do jednej z globalnych współbieżnych kolejek
DispatchQueue.main.async {...} // Wyślij zadanie asynchronicznie do głównego wątku
DispatchQueue.main.sync {...} // Wyślij zadanie synchronicznie do głównego wątku
DispatchQueue.main.asyncAfter (termin: .now () + 3) {...} // Wyślij zadanie asynchronicznie do głównego wątku, który ma zostać wykonany po x sekundach
Szybki <3,0
dispatch_get_main_queue () // Uruchom kolejkę główną w głównym wątku
dispatch_get_global_queue (dispatch_queue_priority_t, 0) // Uzyskaj kolejkę globalną o określonym priorytecie dispatch_queue_priority_t
dispatch_async (dispatch_queue_t) {() -> Void in ...} // Wyślij zadanie asynchronicznie na podanym dispatch_queue_t
dispatch_sync (dispatch_queue_t) {() -> Void in ...} // Wyślij zadanie synchronicznie na podanym dispatch_queue_t
dispatch_after (dispatch_time (DISPATCH_TIME_NOW, Int64 (nanosekundy)), dispatch_queue_t, {...}); // Wyślij zadanie na określony dispque_queue_t po nanosekundach
Uzyskiwanie kolejki Grand Central Dispatch (GCD)
Grand Central Dispatch pracuje nad koncepcją „Kolejki wysyłkowe”. Kolejka wysyłkowa wykonuje wyznaczone zadania w kolejności, w jakiej zostały przekazane. Istnieją trzy typy kolejek wysyłkowych:
- Szeregowe kolejki wysyłkowe ( znane również jako prywatne kolejki wysyłkowe) wykonują kolejno jedno zadanie. Są często używane do synchronizacji dostępu do zasobu.
- Współbieżne kolejki wysyłkowe (inaczej globalne kolejki wysyłkowe) wykonują jedno lub więcej zadań jednocześnie.
- Główna kolejka wysyłkowa wykonuje zadania w głównym wątku.
Aby uzyskać dostęp do głównej kolejki:
let mainQueue = DispatchQueue.main
let mainQueue = dispatch_get_main_queue()
System zapewnia współbieżne globalne kolejki wysyłki (globalne dla aplikacji) o różnych priorytetach. Dostęp do tych kolejek można uzyskać za pomocą klasy DispatchQueue
w Swift 3:
let globalConcurrentQueue = DispatchQueue.global(qos: .default)
równoważny
let globalConcurrentQueue = DispatchQueue.global()
let globalConcurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
W systemie iOS 8 lub nowszym możliwe wartości jakości usług, które można przekazać, to .userInteractive
, .userInitiated
, .default
, .utility
i .background
. Zastępują one stałe DISPATCH_QUEUE_PRIORITY_
.
Możesz także tworzyć własne kolejki o różnych priorytetach:
let myConcurrentQueue = DispatchQueue(label: "my-concurrent-queue", qos: .userInitiated, attributes: [.concurrent], autoreleaseFrequency: .workItem, target: nil)
let mySerialQueue = DispatchQueue(label: "my-serial-queue", qos: .background, attributes: [], autoreleaseFrequency: .workItem, target: nil)
let myConcurrentQueue = dispatch_queue_create("my-concurrent-queue", DISPATCH_QUEUE_CONCURRENT)
let mySerialQueue = dispatch_queue_create("my-serial-queue", DISPATCH_QUEUE_SERIAL)
W Swift 3 kolejki tworzone są z tego initializer seryjny domyślnie i przechodzącej .workItem
dla autorelease częstotliwości zapewnia jest tworzona i odsączone dla każdej pozycji roboczej basen autorelease. Istnieje również .never
, co oznacza, że będziesz sam zarządzał własnymi pulami .inherit
lub .inherit
które dziedziczy ustawienia ze środowiska. W większości przypadków prawdopodobnie nie będziesz używać. .never
z wyjątkiem przypadków skrajnego dostosowania.
Uruchamianie zadań w kolejce Grand Central Dispatch (GCD)
Aby uruchomić zadania w kolejce wysyłki, użyj metod sync
, async
i after
.
Aby asynchronicznie wysłać zadanie do kolejki:
let queue = DispatchQueue(label: "myQueueName")
queue.async {
//do something
DispatchQueue.main.async {
//this will be called in main thread
//any UI updates should be placed here
}
}
// ... code here will execute immediately, before the task finished
Aby synchronicznie wysłać zadanie do kolejki:
queue.sync {
// Do some task
}
// ... code here will not execute until the task is finished
Aby wysłać zadanie do kolejki po określonej liczbie sekund:
queue.asyncAfter(deadline: .now() + 3) {
//this will be executed in a background-thread after 3 seconds
}
// ... code here will execute immediately, before the task finished
UWAGA: Wszelkie aktualizacje interfejsu użytkownika powinny być wywoływane w głównym wątku! Upewnij się, że umieściłeś kod aktualizacji interfejsu użytkownika w
DispatchQueue.main.async { ... }
Rodzaje kolejki:
let mainQueue = dispatch_get_main_queue()
let highQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)
let backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)
Aby asynchronicznie wysłać zadanie do kolejki:
dispatch_async(queue) {
// Your code run run asynchronously. Code is queued and executed
// at some point in the future.
}
// Code after the async block will execute immediately
Aby synchronicznie wysłać zadanie do kolejki:
dispatch_sync(queue) {
// Your sync code
}
// Code after the sync block will wait until the sync task finished
Aby wysłać zadanie po upływie określonego czasu (użyj NSEC_PER_SEC
aby przekonwertować sekundy na nanosekundy):
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(2.5 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) {
// Code to be performed in 2.5 seconds here
}
Aby wykonać zadanie asynchronicznie i zaktualizować interfejs użytkownika:
dispatch_async(queue) {
// Your time consuming code here
dispatch_async(dispatch_get_main_queue()) {
// Update the UI code
}
}
UWAGA: Wszelkie aktualizacje interfejsu użytkownika powinny być wywoływane w głównym wątku! Upewnij się, że umieściłeś kod aktualizacji interfejsu użytkownika wewnątrz
dispatch_async(dispatch_get_main_queue()) { ... }
Równoległe pętle
GCD zapewnia mechanizm wykonywania pętli, dzięki czemu pętle występują równolegle względem siebie. Jest to bardzo przydatne podczas wykonywania serii drogich obliczeniowo obliczeń.
Rozważ tę pętlę:
for index in 0 ..< iterations {
// Do something computationally expensive here
}
Możesz wykonać te obliczenia jednocześnie, używając concurrentPerform
(w Swift 3) lub dispatch_apply
(w Swift 2):
DispatchQueue.concurrentPerform(iterations: iterations) { index in
// Do something computationally expensive here
}
dispatch_apply(iterations, queue) { index in
// Do something computationally expensive here
}
Zamknięcie pętli będzie wywoływane dla każdego index
od 0
do iterations
, ale bez uwzględnienia. Te iteracje będą przebiegać równolegle względem siebie, a zatem kolejność ich uruchamiania nie jest gwarantowana. Rzeczywista liczba iteracji, które zachodzą jednocześnie w danym momencie, jest ogólnie podyktowana możliwościami danego urządzenia (np. Ile rdzeni ma to urządzenie).
Kilka specjalnych uwag:
concurrentPerform
/dispatch_apply
może uruchamiać pętle jednocześnie względem siebie, ale wszystko to dzieje się synchronicznie w odniesieniu do wątku, z którego go wywołujesz. Nie wywołuj tego z głównego wątku, ponieważ spowoduje to zablokowanie tego wątku, dopóki pętla nie zostanie wykonana.Ponieważ pętle te zachodzą równolegle względem siebie, jesteś odpowiedzialny za zapewnienie bezpieczeństwa wątków wyników. Na przykład, jeśli aktualizujesz jakiś słownik o wyniki tych kosztownie obliczeniowych obliczeń, upewnij się, że synchronizujesz te aktualizacje samodzielnie.
Uwaga: podczas uruchamiania równoległych pętli występuje pewien narzut. Tak więc, jeśli obliczenia wykonywane w pętli nie są wystarczająco intensywne obliczeniowo, może się okazać, że wszelka wydajność uzyskana przy użyciu współbieżnych pętli może zostać zmniejszona, jeśli nie całkowicie, zrównoważona przez narzut związany z synchronizacją wszystkich tych współbieżnych wątków.
Odpowiadasz więc za określenie prawidłowej ilości pracy do wykonania w każdej iteracji pętli. Jeśli obliczenia są zbyt proste, możesz zastosować „kroczenie”, aby uwzględnić więcej pracy na pętlę. Na przykład, zamiast wykonywać równoległą pętlę z 1 milionem trywialnych obliczeń, możesz wykonać 100 iteracji w swojej pętli, wykonując 10 000 obliczeń na pętlę. W ten sposób wystarczająca ilość pracy jest wykonywana na każdym wątku, więc narzut związany z zarządzaniem tymi równoległymi pętlami staje się mniej znaczący.
Uruchamianie zadań w OperationQueue
Możesz myśleć o OperationQueue
jako o linii zadań oczekujących na wykonanie. W przeciwieństwie do kolejek wysyłkowych w GCD, kolejki operacji nie są FIFO (pierwsze weszło pierwsze wyszło). Zamiast tego wykonują zadania, gdy tylko są gotowe do wykonania, o ile jest wystarczających zasobów systemowych, aby na to pozwolić.
Uzyskaj główną OperationQueue
:
let mainQueue = OperationQueue.main
Utwórz niestandardową kolejkę OperationQueue
:
let queue = OperationQueue()
queue.name = "My Queue"
queue.qualityOfService = .default
Jakość usługi określa ważność pracy lub to, ile użytkownik może liczyć na natychmiastowe wyniki zadania.
Dodaj Operation
do OperationQueue
:
// An instance of some Operation subclass
let operation = BlockOperation {
// perform task here
}
queue.addOperation(operation)
Dodaj blok do OperationQueue
:
myQueue.addOperation {
// some task
}
Dodaj wiele Operation
do OperationQueue
:
let operations = [Operation]()
// Fill array with Operations
myQueue.addOperation(operations)
Dostosuj liczbę Operation
które mogą być wykonywane jednocześnie w kolejce:
myQueue.maxConcurrentOperationCount = 3 // 3 operations may execute at once
// Sets number of concurrent operations based on current system conditions
myQueue.maxConcurrentOperationCount = NSOperationQueueDefaultMaxConcurrentOperationCount
Zawieszenie kolejki uniemożliwi jej rozpoczęcie wykonywania wszelkich istniejących, niezakończonych operacji lub jakichkolwiek nowych operacji dodanych do kolejki. Sposób na wznowienie tej kolejki polega na ustawieniu isSuspended
powrotem na false
:
myQueue.isSuspended = true
// Re-enable execution
myQueue.isSuspended = false
Zawieszenie OperationQueue
nie zatrzymuje ani nie anuluje operacji, które już są wykonywane. Należy jedynie próbować zawiesić utworzoną przez siebie kolejkę, a nie kolejki globalne lub kolejkę główną.
Tworzenie operacji wysokiego poziomu
Framework Foundation udostępnia typ Operation
, który reprezentuje obiekt wysokiego poziomu, który obejmuje część pracy, którą można wykonać w kolejce. Kolejka nie tylko koordynuje wydajność tych operacji, ale można także ustalić zależności między operacjami, tworzyć operacje, które można anulować, ograniczać stopień współbieżności stosowanej przez kolejkę operacji itp.
Operation
jest gotowa do wykonania po zakończeniu wykonywania wszystkich jej zależności. Następnie właściwość isReady
zmienia się na true
.
Utwórz prostą niesąsiadującą podklasę Operation
:
class MyOperation: Operation {
init(<parameters>) {
// Do any setup work here
}
override func main() {
// Perform the task
}
}
class MyOperation: NSOperation {
init(<parameters>) {
// Do any setup work here
}
override func main() {
// Perform the task
}
}
Dodaj operację do OperationQueue
:
myQueue.addOperation(operation)
Spowoduje to wykonanie operacji równolegle w kolejce.
Zarządzaj zależnościami od Operation
.
Zależności definiują inne Operation
które muszą zostać wykonane w kolejce, zanim Operation
zostanie uznana za gotową do wykonania.
operation2.addDependency(operation1)
operation2.removeDependency(operation1)
Uruchom Operation
bez kolejki:
operation.start()
Zależności zostaną zignorowane. Jeśli jest to operacja współbieżna, zadanie może być nadal wykonywane jednocześnie, jeśli jego metoda start
odciąża pracę do kolejek w tle.
Współbieżne operacje.
Jeśli zadanie, które ma wykonać Operation
, samo w sobie jest asynchroniczne (np. URLSession
danych URLSession
), należy zaimplementować Operation
jako operację współbieżną. W takim przypadku twoja implementacja isAsynchronous
powinna zwrócić true
, na ogół masz metodę start
, która wykonuje pewne ustawienia, a następnie wywołuje swoją main
metodę, która faktycznie wykonuje zadanie.
Przy wdrażaniu asynchroniczny Operation
rozpoczyna trzeba wdrożyć isExecuting
, isFinished
metod i KVO. Kiedy więc rozpoczyna się wykonywanie, właściwość isExecuting
zmienia się na true
. Gdy Operation
zakończy swoje zadanie, parametr isExecuting
ma wartość false
, a parametr isFinished
ma wartość true
. Jeśli operacja zostanie anulowana, zarówno isCancelled
jak i isFinished
zmieni się na true
. Wszystkie te właściwości są obserwowalne.
Anuluj Operation
.
Wywołanie cancel
po prostu zmienia właściwość isCancelled
na true
. Aby zareagować na anulowanie w ramach własnej podklasy Operation
, należy co najmniej okresowo sprawdzać wartość isCancelled
w main
i odpowiednio reagować.
operation.cancel()