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.



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow