In attesa del termine dell'attività


99

Come posso fare in modo che il mio codice attenda fino al termine dell'attività in DispatchQueue? Ha bisogno di CompletionHandler o qualcosa del genere?

func myFunction() {
    var a: Int?

    DispatchQueue.main.async {
        var b: Int = 3
        a = b
    }

    // wait until the task finishes, then print 

    print(a) // - this will contain nil, of course, because it
             // will execute before the code above

}

Sto usando Xcode 8.2 e scrivo in Swift 3.

Risposte:


229

Usa DispatchGroups per ottenere questo risultato. Puoi ricevere una notifica quando il gruppo enter()e le leave()chiamate sono bilanciate:

func myFunction() {
    var a: Int?

    let group = DispatchGroup()
    group.enter()

    DispatchQueue.main.async {
        a = 1
        group.leave()
    }

    // does not wait. But the code in notify() gets run 
    // after enter() and leave() calls are balanced

    group.notify(queue: .main) {
        print(a)
    }
}

oppure puoi aspettare:

func myFunction() {
    var a: Int?

    let group = DispatchGroup()
    group.enter()

    // avoid deadlocks by not using .main queue here
    DispatchQueue.global(attributes: .qosDefault).async {
        a = 1
        group.leave()
    }

    // wait ...
    group.wait()

    print(a) // you could also `return a` here
}

Nota : group.wait()blocca la coda corrente (probabilmente la coda principale nel tuo caso), quindi devi farlo dispatch.asyncsu un'altra coda (come nel codice di esempio sopra) per evitare un deadlock .


Voglio eseguire una funzione in un'altra classe ma voglio aspettare di finire quella funzione e poi nella classe corrente continuare come posso gestirla?
Saeed Rahmatolahi

2
@SaeedRahmatolahi: o usa l' waitapproccio (se non è un problema per te bloccare, cioè se non sei nel thread principale) o fornisci un gestore di completamento o usa l'approccio di notifica nella tua classe chiamante.
shallowThought

Perché chiami group.enterfuori dal blocco asincrono? Non dovrebbe essere responsabilità di ogni blocco entrare e uscire dal gruppo?
Bill il

4
@ Bill waitattende fino a quando entere le leavechiamate sono equilibrate. Se si mette enterin chiusura, waitnon sarebbe aspettare perché enternon è ancora stato, e quindi chiamato il numero di entere leavechiamate sono equilibrato (# entrano == 0, # leav == 0).
shallowThought

1
@rustyMagnet per i test, molto probabilmente non è la strada da percorrere. Usa XCTTestExpectationinvece s. Vedi questo codice di esempio
shallowThought

26

In Swift 3, non è necessario un gestore di completamento quando DispatchQueuetermina un'attività. Inoltre puoi raggiungere il tuo obiettivo in diversi modi

Un modo è questo:

    var a: Int?

    let queue = DispatchQueue(label: "com.app.queue")
    queue.sync {

        for  i in 0..<10 {

            print("Ⓜ️" , i)
            a = i
        }
    }

    print("After Queue \(a)")

Aspetterà fino al termine del ciclo, ma in questo caso il thread principale si bloccherà.

Puoi anche fare la stessa cosa in questo modo:

    let myGroup = DispatchGroup()
    myGroup.enter()
    //// Do your task

    myGroup.leave() //// When your task completes
     myGroup.notify(queue: DispatchQueue.main) {

        ////// do your remaining work
    }

Un'ultima cosa: se vuoi usare completamentoHandler quando la tua attività viene completata usando DispatchQueue, puoi usare DispatchWorkItem.

Ecco un esempio su come utilizzare DispatchWorkItem:

let workItem = DispatchWorkItem {
    // Do something
}

let queue = DispatchQueue.global()
queue.async {
    workItem.perform()
}
workItem.notify(queue: DispatchQueue.main) {
    // Here you can notify you Main thread
}

1
Li ho provati tutti e nessuno ha funzionato durante il tentativo di gestire le chiamate Firebase in un ciclo for

2

Usa gruppo di invio

   dispatchGroup.enter()
   FirstOperation(completion: { _ in
dispatchGroup.leave()
  })
    dispatchGroup.enter()
    SecondOperation(completion: { _ in
dispatchGroup.leave()
  })
   dispatchGroup.wait() //Waits here on this thread until the two operations complete executing.

5
Supponendo che lo chiami sulla coda principale, questo causerà un deadlock.
shallowThought

@shallowThought So true.
Prateekro

Voglio eseguire una funzione in un'altra classe ma voglio aspettare di finire quella funzione e poi nella classe corrente continuare come posso gestirla?
Saeed Rahmatolahi

1
Sebbene l'esempio precedente si bloccherà sul thread principale, come hanno mostrato le risposte precedenti, il wrapping all'interno DispatchQueue.global().async{}non bloccherà la coda principale.
JonnyB

2

Versione Swift 5 della soluzione

func myCriticalFunction () {var value1: String? var value2: String?

let group = DispatchGroup()


group.enter()
//async operation 1
DispatchQueue.global(qos: .default).async { 
    // Network calls or some other async task
    value1 = //out of async task
    group.leave()
}


group.enter()
//async operation 2
DispatchQueue.global(qos: .default).async {
    // Network calls or some other async task
    value2 = //out of async task
    group.leave()
}


group.wait()

print("Value1 \(value1) , Value2 \(value2)") 

}


1

Swift 4

È possibile utilizzare la funzione asincrona per queste situazioni. Quando si utilizza DispatchGroup(), a volte può verificarsi un deadlock .

var a: Int?
@objc func myFunction(completion:@escaping (Bool) -> () ) {

    DispatchQueue.main.async {
        let b: Int = 3
        a = b
        completion(true)
    }

}

override func viewDidLoad() {
    super.viewDidLoad()

    myFunction { (status) in
        if status {
            print(self.a!)
        }
    }
}
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.