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.