Come usare il thread in background in swift?


329

Come usare il threading in swift?

dispatchOnMainThread:^{

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

}];

Quale parte hai problemi con la conversione?
nschum,

2
Perché hai ]prima del punto e virgola nell'ultima riga?
Akashivskyy,

3
sarebbe utile se spiegassi dove sei bloccato o con chi hai bisogno di aiuto.
nsuinteger,

4
Devi accettare la risposta corretta se ti aiuta davvero, aiuterà gli altri anche a trovare la soluzione corretta.
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,

Risposte:


708

Swift 3.0+

Molto è stato modernizzato in Swift 3.0. Eseguire qualcosa sul thread in background è simile al seguente:

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")
    }
}

Da Swift 1.2 a 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 - Problema noto

A partire da Swift 1.1, Apple non supportava la sintassi sopra senza alcune modifiche. Il passaggio in QOS_CLASS_BACKGROUNDrealtà non ha funzionato, invece usa Int(QOS_CLASS_BACKGROUND.value).

Per ulteriori informazioni, consultare la documentazione sulle mele


23
E se qualcuno vuole una sintassi più rapida come Swift, ho creato Async che aggiunge un po 'di zucchero alla sintassi simileAsync.background {}
tobiasdm,

Sto usando il tuo codice in xCode 6.0.1 e iOS 8. Fornisce un errore come classe di ritorno "QOS_CLASS_BACKGROUND" ed è di tipo UInt32 e "dispatch_get_global_queue" richiede il 1o parametro come int, quindi l'errore di tipo sta arrivando.
Zalak Patel,

Quindi in Xcode 6.1.1 non ottengo un errore per l'utilizzo del semplice "QOS_CLASS_BACKGROUND". È stato riparato?
Lucas Goossen,

@LucasGoossen Sì, è stato corretto. Ho aggiornato il post di conseguenza.
tobiasdm,

1
@NikitaPronchik Non è chiaro dalla risposta? Altrimenti sentiti libero di modificarlo.
Tobiasdm,

123

La migliore pratica è definire una funzione riutilizzabile a cui è possibile accedere più volte.

FUNZIONE RIUTILIZZABILE:

ad esempio da qualche parte come AppDelegate.swift come funzione globale.

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?()
        }
    }
}

Nota: in Swift 2.0, sostituire QOS_CLASS_USER_INITIATED.value sopra con QOS_CLASS_USER_INITIATED.rawValue invece

USO:

A. Per eseguire un processo in background con un ritardo di 3 secondi:

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

B. Per eseguire un processo in background, quindi eseguire un completamento in primo piano:

    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. Per ritardare di 3 secondi - notare l'uso del parametro di completamento senza parametro di sfondo:

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

1
bel frammento, dovrebbe essere la risposta corretta. @Dale Clifford
LoVo

Brillante approccio moderno di alto livello Swift-y per accedere ai vecchi metodi GCD dalla libreria C di basso livello. Dovrebbe venire standard in Swift.
Craig Grummitt,

2
Molto bella. Conferma, il ritardo funziona solo per il blocco di completamento. Ciò significa che il ritardo in A. non ha alcun impatto e il blocco in background viene eseguito immediatamente senza ritardo.
Obiettivo

1
Dovresti essere in grado di sostituirlo if(background != nil){ background!(); }con background?()una sintassi un po 'più veloce?
Simon Bengtsson,

1
Potresti aggiornare questo per Swift 3? Il convertitore automatico lo ha trasformato in DispatchQueue.global(priority: Int(DispatchQoS.QoSClass.userInitiated.rawValue)).async {ma questo genera un errore simile cannot invoke initializer for type 'Int' with an argument list of type '(qos_class_t)'. Una soluzione funzionante è disponibile qui ( DispatchQueue.global(qos: DispatchQoS.QoSClass.userInitiated).async).
Dev-iL,

111

La risposta di Dan Beaulieu in swift5 (funzionante anche da swift 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()
                })
            }
        }
    }

}

uso

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
})

Incredibile, grazie per aver aggiornato così bene il formato Swift 3.0.1!
Dale Clifford,

1
Uso le estensioni più di qualsiasi altra persona vivente. Ma esiste un vero pericolo nell'uso di un'estensione che non è affatto diversa dall'originale!
Fattie,

@Frouo Molto elegante, è possibile aggiungere un gestore di completamento a quando 4 chiamate asincrone finiscono? So che è un po 'fuori tema.
eonista,

1
dimentichi quel link. tutto ciò che serve è un gruppo di spedizione - è molto molto semplice; nessuna preoccupazione!
Fattie,

1
@DilipJangid non puoi, a meno che il tuo lavoro nella backgroundchiusura non sia molto molto molto lungo (~ = infinito). Questo metodo è progettato per durare per un tempo limitato: il tempo necessario per l'esecuzione del processo in background. Pertanto, la completionchiusura verrà richiamata non appena il tempo di esecuzione del processo in background + ritardo è trascorso.
frouo

42

Versione Swift 3

Swift 3 utilizza una nuova DispatchQueueclasse per gestire code e thread. Per eseguire qualcosa sul thread in background devi usare:

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

O se vuoi qualcosa in due righe di codice:

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."
    }
}

In questo tutorial puoi anche ottenere informazioni approfondite su GDC in Swift 3 .


Disse. Poiché la tua risposta è la migliore, ho inserito una riga di codice che mostra come "richiami quando hai finito". Sentiti libero di rilassarti o modificare, evviva
Fattie,

35

Dal tutorial di Jameson Quave

Swift 2

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

3
Solo per chiarimenti, perché dovrebbe essere usato al posto della risposta accettata? È solo un'API precedente?
Sirene,

1
@Sirens Penso che sarebbe molto utile per le app che supportano <iOS 8.
bperdue,

Lo uso per iOs 8.2 per forzare i processi.
μολὼν.λαβέ

DISPATCH_QUEUE_PRIORITY_DEFAULT ripristina QOS_CLASS_DEFAULT. Quindi suppongo che si possa dire che è sintassi di livello superiore / accettata.
PostCodeism,

34

In Swift 4.2 e Xcode 10.1

Abbiamo tre tipi di code:

1. Coda principale: coda principale è una coda seriale creata dal sistema e associata al thread principale dell'applicazione.

2. Coda globale: la coda globale è una coda simultanea che possiamo richiedere in relazione alla priorità delle attività.

3. Code personalizzate: possono essere create dall'utente. Le code simultanee personalizzate sono sempre associate a una delle code globali specificando una proprietà 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

Queste tutte le code possono essere eseguite in due modi

1. Esecuzione sincrona

2. Esecuzione asincrona

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()
})

Da 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
Miglior tutorial per discussioni medium.com/@gabriel_lewis/…
iOS

Non ho visto alcun cambiamento quando usi .background QoS o, .userInitiatedma per me ha funzionato con.background
ruggine

24

Swift 4.x

Metti questo in alcuni file:

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

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

e poi chiamalo dove ti serve:

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

22

Devi separare le modifiche che desideri eseguire in background dagli aggiornamenti che desideri eseguire sull'interfaccia utente:

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

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

Quindi dispatch_async(dispatch_get_main_queue()) { // update some UI }viene chiamato al termine dell'esecuzione dell'istruzione in background (Outer Block)?
justColbs

Non è solo per Swift 2.3 e precedenti?
Surz,

9

Buone risposte, comunque, voglio condividere la mia soluzione orientata agli oggetti aggiornata per swift 5 .

per favore dai un'occhiata: AsyncTask

Concettualmente ispirato da AsyncTask di Android, ho scritto la mia classe in Swift

AsyncTask consente un uso corretto e semplice del thread dell'interfaccia utente. Questa classe consente di eseguire operazioni in background e pubblicare risultati sul thread dell'interfaccia utente.

Ecco alcuni esempi di utilizzo

Esempio 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'

Esempio 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

Ha 2 tipi generici:

  • BGParam - il tipo di parametro inviato all'attività al momento dell'esecuzione.

  • BGResult - il tipo del risultato del calcolo in background.

    Quando crei un AsyncTask puoi quei tipi a qualunque cosa tu abbia bisogno per passare dentro e fuori l'attività in background, ma se non hai bisogno di quei tipi, puoi contrassegnarlo come inutilizzato semplicemente impostandolo su: Voido con una sintassi più breve:()

Quando viene eseguita un'attività asincrona, passa attraverso 3 passaggi:

  1. beforeTask:()->Void invocato sul thread dell'interfaccia utente subito prima dell'esecuzione dell'attività.
  2. backgroundTask: (param:BGParam)->BGResult invocato sul thread in background immediatamente dopo
  3. afterTask:(param:BGResult)->Void invocato sul thread dell'interfaccia utente con il risultato dell'attività in background

4
Questo funziona meravigliosamente per me. Bel lavoro, perché non metterlo su Github?
36 By Design

8

Poiché alla domanda OP è già stata data una risposta sopra, voglio solo aggiungere alcune considerazioni sulla velocità:

Non consiglio di eseguire attività con la priorità del thread .background in particolare su iPhone X in cui l'attività sembra essere allocata sui core a bassa potenza.

Ecco alcuni dati reali da una funzione ad alta intensità computazionale che legge da un file XML (con buffering) ed esegue l'interpolazione dei dati:

Nome dispositivo / .background / .utility / .default / .userInitiated / .userInteractive

  1. iPhone X: 18.7s / 6.3s / 1.8s / 1.8s / 1.8s
  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

Si noti che il set di dati non è lo stesso per tutti i dispositivi. È il più grande su iPhone X e il più piccolo su iPhone 5s.


4

Swift 5

Per semplificare, crea un file "DispatchQueue + Extensions.swift" con questo contenuto:

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()
        }
    }
}

Utilizzo:

Dispatch.background {
    // do stuff

    Dispatch.main { 
        // update UI
    }
}

2

Grand Central Dispatch viene utilizzato per gestire il multitasking nelle nostre app iOS.

Puoi usare questo codice

// 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)
}

Maggiori informazioni utilizzano questo link: https://www.programminghub.us/2018/07/integrate-dispatcher-in-swift.html


2

Funzione multiuso per filo

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)
    }

Usalo come:

performOn(.Background) {
    //Code
}

1

Mi piace molto la risposta di Dan Beaulieu, ma non funziona con Swift 2.2 e penso che possiamo evitare quei brutti scappamenti forzati!

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

in Swift 4.2 funziona.

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);
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.