Jak używać wątku w tle w trybie szybkim?


329

Jak korzystać z wątków w trybie szybkim?

dispatchOnMainThread:^{

    NSLog(@"Block Executed On %s", dispatch_queue_get_label(dispatch_get_current_queue()));

}];

Którą część masz problem z konwersją?
nschum

2
Dlaczego masz ]przed średnikiem w ostatnim wierszu?
akashivskyy

3
byłoby pomocne, gdybyś wyjaśnił, gdzie utknąłeś lub w czym potrzebujesz pomocy.
nsuinteger

4
Musisz zaakceptować poprawną odpowiedź, jeśli naprawdę ci to pomoże, pomoże innym również znaleźć właściwe rozwiązanie.
Amit Singh,

DispatchQueue.global(qos: .background).async { print("Run on background thread") DispatchQueue.main.async { print("We finished that.") // only back on the main thread, may you access UI: label.text = "Done." } }
Anurag Sharma

Odpowiedzi:


708

Swift 3.0+

Wiele zostało zmodernizowane w Swift 3.0. Uruchomienie czegoś w wątku w tle wygląda następująco:

DispatchQueue.global(qos: .background).async {
    print("This is run on the background queue")

    DispatchQueue.main.async {
        print("This is run on the main queue, after the previous code in outer block")
    }
}

Swift 1.2 do 2.3

let qualityOfServiceClass = QOS_CLASS_BACKGROUND
let backgroundQueue = dispatch_get_global_queue(qualityOfServiceClass, 0)
dispatch_async(backgroundQueue, {
    print("This is run on the background queue")

    dispatch_async(dispatch_get_main_queue(), { () -> Void in
        print("This is run on the main queue, after the previous code in outer block")
    })
})

Pre Swift 1.2 - Znany problem

Począwszy od Swift 1.1 Apple nie obsługiwał powyższej składni bez pewnych modyfikacji. Przekazywanie QOS_CLASS_BACKGROUNDnie zadziałało, zamiast tego użyj Int(QOS_CLASS_BACKGROUND.value).

Aby uzyskać więcej informacji, zobacz dokumentację jabłek


23
A jeśli ktoś chce bardziej składni podobnej do Swift, stworzyłem Async, który dodaje trochę cukru do składni, np.Async.background {}
tobiasdm,

Używam twojego kodu w xCode 6.0.1 i iOS 8. Daje błąd jako klasa zwracana „QOS_CLASS_BACKGROUND” i jest typu UInt32, a „dispatch_get_global_queue” wymaga 1. parametru, ponieważ nadchodzi błąd typu.
Zalak Patel

Tak więc w Xcode 6.1.1 nie pojawia się błąd przy użyciu zwykłego prostego „QOS_CLASS_BACKGROUND”. Czy to jest naprawione?
Lucas Goossen

@LucasGoossen Tak, zostało to naprawione. Zaktualizowałem odpowiednio post.
tobiasdm

1
@NikitaPronchik Czy nie wynika to jasno z odpowiedzi? W przeciwnym razie możesz go edytować.
tobiasdm

123

Najlepszą praktyką jest zdefiniowanie funkcji wielokrotnego użytku, do której można uzyskać dostęp wielokrotnie.

FUNKCJA wielokrotnego użytku:

np. gdzieś jak AppDelegate.swift jako funkcja globalna.

func backgroundThread(_ delay: Double = 0.0, background: (() -> Void)? = nil, completion: (() -> Void)? = nil) {
    dispatch_async(dispatch_get_global_queue(Int(QOS_CLASS_USER_INITIATED.value), 0)) {
        background?()

        let popTime = dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC)))
        dispatch_after(popTime, dispatch_get_main_queue()) {
            completion?()
        }
    }
}

Uwaga: w Swift 2.0 zastąpić QOS_CLASS_USER_INITIATED.value powyżej QOS_CLASS_USER_INITIATED.rawValue zamiast

STOSOWANIE:

A. Aby uruchomić proces w tle z opóźnieniem 3 sekund:

    backgroundThread(3.0, background: {
            // Your background function here
    })

B. Aby uruchomić proces w tle, a następnie uruchom ukończenie na pierwszym planie:

    backgroundThread(background: {
            // Your function here to run in the background
    },
    completion: {
            // A function to run in the foreground when the background thread is complete
    })

C. Aby opóźnić o 3 sekundy - zwróć uwagę na użycie parametru zakończenia bez parametru tła:

    backgroundThread(3.0, completion: {
            // Your delayed function here to be run in the foreground
    })

1
ładny fragment, powinien być poprawną odpowiedzią. @Dale Clifford
LoVo

Genialne, nowoczesne, szybkie podejście do szybkiego dostępu do starych metod GCD z niskopoziomowej biblioteki C. Powinny być standardem w Swift.
Craig Grummitt,

2
Bardzo dobrze. Proszę potwierdzić, że opóźnienie działa tylko w przypadku bloku uzupełniającego. Oznacza to, że opóźnienie w A. nie ma wpływu, a blok tła wykonuje się natychmiast bez opóźnienia.
ObjectiveTC

1
Powinieneś być w stanie wymienić if(background != nil){ background!(); }z background?()na nieco swiftier składni?
Simon Bengtsson,

1
Czy mógłbyś zaktualizować to dla Swift 3? Automatyczny konwerter zmienił go w, DispatchQueue.global(priority: Int(DispatchQoS.QoSClass.userInitiated.rawValue)).async {ale generuje to błąd podobny do cannot invoke initializer for type 'Int' with an argument list of type '(qos_class_t)'. Działające rozwiązanie znajduje się tutaj ( DispatchQueue.global(qos: DispatchQoS.QoSClass.userInitiated).async).
Dev-iL

111

Odpowiedź Dana Beaulieu w swift5 (działa również od wersji 3.0.1).

Swift 5.0.1

extension DispatchQueue {

    static func background(delay: Double = 0.0, background: (()->Void)? = nil, completion: (() -> Void)? = nil) {
        DispatchQueue.global(qos: .background).async {
            background?()
            if let completion = completion {
                DispatchQueue.main.asyncAfter(deadline: .now() + delay, execute: {
                    completion()
                })
            }
        }
    }

}

Stosowanie

DispatchQueue.background(delay: 3.0, background: {
    // do something in background
}, completion: {
    // when background job finishes, wait 3 seconds and do something in main thread
})

DispatchQueue.background(background: {
    // do something in background
}, completion:{
    // when background job finished, do something in main thread
})

DispatchQueue.background(delay: 3.0, completion:{
    // do something in main thread after 3 seconds
})

Niesamowite, dziękuję za tak ładną aktualizację do formatu Swift 3.0.1!
Dale Clifford,

1
Używam rozszerzeń bardziej niż jakiejkolwiek żywej osoby. Ale istnieje prawdziwe niebezpieczeństwo przy użyciu rozszerzenia, które wcale nie różni się od oryginału!
Fattie

@Frouo Bardzo elegancko, czy można dodać moduł obsługi zakończenia, gdy wszystkie 4 połączenia asynchroniczne zakończą się? Wiem, że to trochę nie na temat.
eonista

1
zapomnij ten link. wystarczy grupa wysyłkowa - to bardzo, bardzo proste; nie martw się tym !
Fattie

1
@DilipJangid nie możesz, chyba że twoja praca w backgroundzamknięciu jest bardzo, bardzo, bardzo długa (~ = nieskończona). Ta metoda ma trwać przez określony czas: czas potrzebny na wykonanie zadania w tle. Tak więc completionzamknięcie zostanie wywołane, gdy minie czas wykonania zadania w tle + opóźnienie.
frouo

42

Wersja Swift 3

Swift 3 wykorzystuje nową DispatchQueueklasę do zarządzania kolejkami i wątkami. Aby uruchomić coś w wątku w tle, użyj:

let backgroundQueue = DispatchQueue(label: "com.app.queue", qos: .background)
backgroundQueue.async {
    print("Run on background thread")
}

Lub jeśli chcesz coś w dwóch wierszach kodu:

DispatchQueue.global(qos: .background).async {
    print("Run on background thread")

    DispatchQueue.main.async {
        print("We finished that.")
        // only back on the main thread, may you access UI:
        label.text = "Done."
    }
}

Możesz także uzyskać szczegółowe informacje o GDC w Swift 3 w tym samouczku .


Powiedziany. Ponieważ twoja odpowiedź jest najlepsza, wrzuciłem wiersz kodu pokazujący, jak „oddzwonisz, kiedy skończysz”. Zapraszam do odpoczynku lub edycji, na zdrowie
Fattie

35

Z samouczka Jamesona Quave'a

Swift 2

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
    //All stuff here
})

3
Tylko dla wyjaśnienia, dlaczego miałoby to być użyte zamiast przyjętej odpowiedzi? Czy to tylko starszy interfejs API?
Syreny

1
@Sirens Myślę, że byłoby to bardzo przydatne dla aplikacji obsługujących <iOS 8.
bperdue

Używam tego dla iOS 8.2 do wymuszenia procesów.
μολὼν.λαβέ

DISPATCH_QUEUE_PRIORITY_DEFAULT powraca do QOS_CLASS_DEFAULT. Sądzę więc, że można powiedzieć, że jest to składnia wysokiego poziomu / akceptowana.
PostCodeism,

34

W Swift 4.2 i Xcode 10.1

Mamy trzy typy kolejek:

1. Kolejka główna: kolejka główna to kolejka szeregowa, która jest tworzona przez system i powiązana z głównym wątkiem aplikacji.

2. Globalna kolejka: globalna kolejka to współbieżna kolejka, którą możemy zażądać w odniesieniu do priorytetu zadań.

3. Kolejki niestandardowe: może tworzyć użytkownik. Niestandardowe współbieżne kolejki zawsze są mapowane na jedną z globalnych kolejek poprzez określenie właściwości Quality of Service (QoS).

DispatchQueue.main//Main thread
DispatchQueue.global(qos: .userInitiated)// High Priority
DispatchQueue.global(qos: .userInteractive)//High Priority (Little Higher than userInitiated)
DispatchQueue.global(qos: .background)//Lowest Priority
DispatchQueue.global(qos: .default)//Normal Priority (after High but before Low)
DispatchQueue.global(qos: .utility)//Low Priority
DispatchQueue.global(qos: .unspecified)//Absence of Quality

Te wszystkie kolejki można wykonać na dwa sposoby

1. Synchroniczne wykonanie

2. Wykonanie asynchroniczne

DispatchQueue.global(qos: .background).async {
    // do your job here
    DispatchQueue.main.async {
        // update ui here
    }
}

//Perform some task and update UI immediately.
DispatchQueue.global(qos: .userInitiated).async {  
    // Perform task
    DispatchQueue.main.async {  
        // Update UI
        self.tableView.reloadData()  
    }
}

//To call or execute function after some time
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
    //Here call your function
}

//If you want to do changes in UI use this
DispatchQueue.main.async(execute: {
    //Update UI
    self.tableView.reloadData()
})

Od AppCoda: https://www.appcoda.com/grand-central-dispatch/

//This will print synchronously means, it will print 1-9 & 100-109
func simpleQueues() {
    let queue = DispatchQueue(label: "com.appcoda.myqueue")

    queue.sync {
        for i in 0..<10 {
            print("🔴", i)
        }
    }

    for i in 100..<110 {
        print("Ⓜ️", i)
    }
}

//This will print asynchronously 
func simpleQueues() {
    let queue = DispatchQueue(label: "com.appcoda.myqueue")

    queue.async {
        for i in 0..<10 {
            print("🔴", i)
        }
    }

    for i in 100..<110 {
        print("Ⓜ️", i)
    }
}

1
Najlepszy samouczek dla wątków medium.com/@gabriel_lewis/…
iOS

Nie widziałem żadnych zmian, kiedy używasz .background QoS, .userInitiatedale dla mnie to zadziałało.background
zardzewiał

24

Swift 4.x

Umieść to w jakimś pliku:

func background(work: @escaping () -> ()) {
    DispatchQueue.global(qos: .userInitiated).async {
        work()
    }
}

func main(work: @escaping () -> ()) {
    DispatchQueue.main.async {
        work()
    }
}

i nazwij to tam, gdzie potrzebujesz:

background {
     //background job
     main {
       //update UI (or what you need to do in main thread)
     }
}

22

Musisz oddzielić zmiany, które chcesz uruchomić w tle, od aktualizacji, które chcesz uruchomić w interfejsie użytkownika:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
    // do your task

    dispatch_async(dispatch_get_main_queue()) {
        // update some UI
    }
}

Tak dispatch_async(dispatch_get_main_queue()) { // update some UI }się nazywa, gdy instrukcja w tle (blok zewnętrzny) jest wykonywana?
justColbs,

Czy to nie dotyczy tylko Swift 2.3 i niższych?
Surz

9

Dobre odpowiedzi, w każdym razie chcę udostępnić moje rozwiązanie zorientowane obiektowo na bieżąco dla szybkiego 5 .

sprawdź to: AsyncTask

Zainspirowany koncepcyjnie Androidem AsyncTask, napisałem własną klasę w Swift

AsyncTask umożliwia prawidłowe i łatwe korzystanie z wątku interfejsu użytkownika. Ta klasa umożliwia wykonywanie operacji w tle i publikowanie wyników w wątku interfejsu użytkownika.

Oto kilka przykładów użycia

Przykład 1 -

AsyncTask(backgroundTask: {(p:String)->Void in//set BGParam to String and BGResult to Void
        print(p);//print the value in background thread
    }).execute("Hello async");//execute with value 'Hello async'

Przykład 2 -

let task2=AsyncTask(beforeTask: {
           print("pre execution");//print 'pre execution' before backgroundTask
        },backgroundTask:{(p:Int)->String in//set BGParam to Int & BGResult to String
            if p>0{//check if execution value is bigger than zero
               return "positive"//pass String "poitive" to afterTask
            }
            return "negative";//otherwise pass String "negative"
        }, afterTask: {(p:String) in
            print(p);//print background task result
    });
    task2.execute(1);//execute with value 1

Ma 2 rodzaje ogólne:

  • BGParam - typ parametru wysyłanego do zadania po wykonaniu.
  • BGResult - rodzaj wyniku obliczeń w tle.

    Podczas tworzenia zadania AsyncTask możesz przypisać te typy do wszystkiego, co potrzebujesz przekazać i wyjść z zadania w tle, ale jeśli nie potrzebujesz tych typów, możesz oznaczyć je jako nieużywane, ustawiając je na: Voidlub krótszą składnią:()

Po wykonaniu zadania asynchronicznego przechodzi on przez 3 kroki:

  1. beforeTask:()->Void wywoływane w wątku interfejsu użytkownika tuż przed wykonaniem zadania.
  2. backgroundTask: (param:BGParam)->BGResult wywoływane w wątku tła natychmiast po
  3. afterTask:(param:BGResult)->Void wywoływane w wątku interfejsu użytkownika z wynikiem z zadania w tle

4
Działa to dla mnie wspaniale. Dobra robota, dlaczego nie umieścić na githubie?
36 Wg projektu

8

Ponieważ odpowiedź na pytanie OP została już udzielona powyżej, chciałbym dodać kilka uwag dotyczących prędkości:

Nie polecam uruchamiania zadań z priorytetem wątku .background, szczególnie na iPhonie X, gdzie zadanie wydaje się być przydzielone do rdzeni o niskiej mocy.

Oto kilka rzeczywistych danych z funkcji intensywnie obliczeniowej, która czyta z pliku XML (z buforowaniem) i wykonuje interpolację danych:

Nazwa urządzenia / .background / .utility / .default / .userInitiated / .userInteractive

  1. iPhone X: 18,7 s / 6,3 s / 1,8 s / 1,8 s / 1,8 s
  2. iPhone 7: 4.6s / 3.1s / 3.0s / 2.8s / 2.6s
  3. iPhone 5s: 7,3s / 6,1s / 4,0s / 4,0s / 3,8s

Pamiętaj, że zestaw danych nie jest taki sam dla wszystkich urządzeń. Jest to największy na iPhone X i najmniejszy na iPhone 5s.


4

Szybki 5

Aby to ułatwić, utwórz plik „DispatchQueue + Extensions.swift” z tą zawartością:

import Foundation

typealias Dispatch = DispatchQueue

extension Dispatch {

    static func background(_ task: @escaping () -> ()) {
        Dispatch.global(qos: .background).async {
            task()
        }
    }

    static func main(_ task: @escaping () -> ()) {
        Dispatch.main.async {
            task()
        }
    }
}

Stosowanie :

Dispatch.background {
    // do stuff

    Dispatch.main { 
        // update UI
    }
}

2

Grand Central Dispatch służy do obsługi wielozadaniowości w naszych aplikacjach na iOS.

Możesz użyć tego kodu

// Using time interval

DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()+1) {
    print("Hello World")
}

// Background thread
queue.sync {
     for i in 0..<10 {
          print("Hello", i)
     }
}

// Main thread
for i in 20..<30 {
     print("Hello", i)
}

Więcej informacji za pomocą tego linku: https://www.programminghub.us/2018/07/integrate-dispatcher-in-swift.html


2

Funkcja wielofunkcyjna dla gwintu

public enum QueueType {
        case Main
        case Background
        case LowPriority
        case HighPriority

        var queue: DispatchQueue {
            switch self {
            case .Main:
                return DispatchQueue.main
            case .Background:
                return DispatchQueue(label: "com.app.queue",
                                     qos: .background,
                                     target: nil)
            case .LowPriority:
                return DispatchQueue.global(qos: .userInitiated)
            case .HighPriority:
                return DispatchQueue.global(qos: .userInitiated)
            }
        }
    }

    func performOn(_ queueType: QueueType, closure: @escaping () -> Void) {
        queueType.queue.async(execute: closure)
    }

Używaj go jak:

performOn(.Background) {
    //Code
}

1

Naprawdę podoba mi się odpowiedź Dana Beaulieu, ale nie działa ona w Swift 2.2 i myślę, że możemy uniknąć tych paskudnych wymuszonych operacji odwijania!

func backgroundThread(delay: Double = 0.0, background: (() -> Void)? = nil, completion: (() -> Void)? = nil) {

    dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0)) {

        background?()

        if let completion = completion{
            let popTime = dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC)))
            dispatch_after(popTime, dispatch_get_main_queue()) {
                completion()
            }
        }
    }
}

0
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), {
    // Conversion into base64 string
    self.uploadImageString =  uploadPhotoDataJPEG.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.EncodingEndLineWithCarriageReturn)
})

-3

w Swift 4.2 to działa.

import Foundation

class myThread: Thread
{
    override func main() {
        while(true) {
            print("Running in the Thread");
            Thread.sleep(forTimeInterval: 4);
        }
    }
}

let t = myThread();
t.start();

while(true) {
    print("Main Loop");
    sleep(5);
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.