Görev bitene kadar bekliyorum


100

DispatchQueue'daki görev bitene kadar kodumu nasıl bekletebilirim? CompletionHandler veya başka bir şeye ihtiyacı var mı?

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

}

Xcode 8.2 kullanıyorum ve Swift 3 ile yazıyorum.

Yanıtlar:


233

DispatchGroupBunu başarmak için s kullanın . Grubun enter()ve leave()aramalar dengelendiğinde bildirim alabilirsiniz :

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

veya bekleyebilirsiniz:

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
}

Not : group.wait()mevcut kuyruğu engeller (muhtemelen sizin durumunuzdaki ana kuyruk), bu nedenle dispatch.asyncbir kilitlenmeyi önlemek için başka bir kuyrukta (yukarıdaki örnek kodda olduğu gibi) olmanız gerekir .


Başka bir sınıfta bir işlevi çalıştırmak istiyorum, ancak bu işlevi bitirmek için beklemek istiyorum ve sonra mevcut sınıfta devam et bunu nasıl halledebilirim?
— Saeed Rahmatolahi

2
@SaeedRahmatolahi: Ya waityaklaşımı kullanın (engellemeniz sorun değilse, yani ana iş parçacığında değilseniz) ya da bir tamamlama işleyicisi sağlayın ya da çağıran sınıfınızda bildirim yaklaşımını kullanın.
— sığ Düşünce

Neden zaman group.enteruyumsuz bloğun dışını arıyorsun? Gruba girip çıkmak her bloğun sorumluluğunda olmamalı mı?
— Bill

4
@ Bill waitkadar bekler enterve leavearamalar dengelenir. Eğer koyarsanız enterkapanmasında waitçünkü beklemek olmaz entersayısı henüz ve böylece çağrılmadıysa enterve leaveçağrılar edilir dengeli (# == 0 girin # leav == 0).
— sığThought

1
@rustyMagnet testler için büyük olasılıkla gidilecek yol değildir. Kullanım XCTTestExpectationyerine s. Bu örnek koda
— sığThought

26

Swift 3'te, DispatchQueuebir görevi bitirdiğinde tamamlama işleyicisine gerek yoktur . Ayrıca hedefinize farklı şekillerde ulaşabilirsiniz

Bunun bir yolu şudur:

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

Döngü bitene kadar bekleyecektir, ancak bu durumda ana iş parçacığınız bloke olacaktır.

Aynı şeyi şu şekilde de yapabilirsiniz:

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

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

        ////// do your remaining work
    }

Son bir şey: Göreviniz DispatchQueue ile tamamlandığında completeHandler'ı kullanmak istiyorsanız, kullanabilirsiniz DispatchWorkItem.

İşte nasıl kullanılacağına bir örnek 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
Firebase çağrılarını for döngüsünde işlemeye çalışırken hepsini denedim ve hiçbiri işe yaramadı

3

Çözümün Swift 5 versiyonu

func myCriticalFunction () {var değer1: Dize? 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)") 

}


2

Sevk grubu kullan

   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
Bunu ana kuyrukta çağırdığınızı varsayarsak, bu bir kilitlenmeye neden olacaktır.
— sığThought

@shallowThought Çok doğru.
— Prateekro

Başka bir sınıfta bir işlevi çalıştırmak istiyorum, ancak bu işlevi bitirmek için beklemek istiyorum ve sonra mevcut sınıfta devam et bunu nasıl halledebilirim?
— Saeed Rahmatolahi

1
Yukarıdaki örnek ana iş parçacığını engelleyecek olsa da, önceki yanıtların da gösterdiği gibi, içine sarmak DispatchQueue.global().async{}ana kuyruğu engellemeyecektir.
— JonnyB

1

Swift 4

Bu durumlar için Async Function'ı kullanabilirsiniz. Kullandığınızda DispatchGroup(), Bazen kilitlenme meydana gelebilir.

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!)
        }
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.