รอจนกว่างานจะเสร็จสิ้น


101

ฉันจะทำให้รหัสของฉันรอจนกว่างานใน DispatchQueue จะเสร็จสิ้นได้อย่างไร มันต้องการ CompletionHandler หรืออะไร?

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 และเขียนใน Swift 3

คำตอบ:


239

ใช้DispatchGroups เพื่อบรรลุสิ่งนี้ คุณสามารถรับการแจ้งเตือนเมื่อกลุ่มenter()และการleave()โทรสมดุลกัน:

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

หรือรอ:

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
}

หมายเหตุ : group.wait()บล็อกคิวปัจจุบัน (อาจจะเป็นคิวที่สำคัญในกรณีของคุณ) เพื่อให้คุณได้dispatch.asyncในคิวอื่น (เช่นในข้างต้นโค้ดตัวอย่าง) เพื่อหลีกเลี่ยงการหยุดชะงัก


ฉันต้องการเรียกใช้ฟังก์ชันในคลาสอื่น แต่ฉันต้องการรอให้ฟังก์ชันนั้นเสร็จสิ้นจากนั้นในคลาสปัจจุบันฉันจะจัดการกับสิ่งนั้นได้อย่างไร
Saeed Rahmatolahi

2
@SaeedRahmatolahi: ใช้waitวิธีการใดก็ได้ (หากไม่มีปัญหาสำหรับคุณในการบล็อกเช่นถ้าคุณไม่ได้อยู่ในเธรดหลัก) หรือจัดเตรียมตัวจัดการที่สมบูรณ์หรือใช้วิธีการแจ้งในคลาสการโทรของคุณ
ตื้นคิด

ทำไมคุณโทรgroup.enterออกนอกบล็อก async การเข้าและออกจากกลุ่มเป็นความรับผิดชอบของแต่ละบล็อกไม่ใช่หรือ?
บิล

4
@ Bill waitรอจนกว่าenterและleaveสายมีความสมดุล หากคุณenterทำการปิดwaitจะไม่รอเพราะenterยังไม่ได้รับการโทรดังนั้นจำนวนenterและการleaveโทรจึงสมดุลกัน (# enter == 0, # leav == 0)
ตื้นคิด

1
@rustyMagnet สำหรับการทดสอบนี่อาจไม่ใช่วิธีที่จะไป ใช้XCTTestExpectations แทน ดูตัวอย่างรหัสนี้
shallowThought

26

ใน Swift 3 ไม่จำเป็นต้องมีตัวจัดการที่เสร็จสมบูรณ์เมื่อDispatchQueueทำงานหนึ่งงานเสร็จ นอกจากนี้คุณสามารถบรรลุเป้าหมายด้วยวิธีต่างๆ

ทางเดียวคือ:

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

จะรอจนกว่าการวนซ้ำจะเสร็จสิ้น แต่ในกรณีนี้เธรดหลักของคุณจะบล็อก

คุณสามารถทำสิ่งเดียวกันนี้ได้เช่นกัน:

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

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

        ////// do your remaining work
    }

สิ่งสุดท้าย: หากคุณต้องการใช้ completeHandler เมื่องานของคุณเสร็จสมบูรณ์โดยใช้ DispatchQueue คุณสามารถใช้DispatchWorkItem.

นี่คือตัวอย่างวิธีใช้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 ในห่วง

2

ใช้กลุ่มการจัดส่ง

   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
สมมติว่าคุณเรียกสิ่งนี้ในคิวหลักสิ่งนี้จะทำให้เกิดการชะงักงัน
ตื้นคิด

@shallow คิดว่าเป็นความจริง
Prateekro

ฉันต้องการเรียกใช้ฟังก์ชันในคลาสอื่น แต่ฉันต้องการรอให้ฟังก์ชันนั้นเสร็จสิ้นจากนั้นในคลาสปัจจุบันฉันจะจัดการกับสิ่งนั้นได้อย่างไร
Saeed Rahmatolahi

1
แม้ว่าตัวอย่างข้างต้นจะบล็อกบนเธรดหลักตามที่แสดงคำตอบก่อนหน้านี้การตัดภายในDispatchQueue.global().async{}จะไม่บล็อกคิวหลัก
JonnyB

2

โซลูชันเวอร์ชัน Swift 5

func myCriticalFunction () {var value1: String? var value2: สตริง?

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

สวิฟต์ 4

คุณสามารถใช้ Async Function สำหรับสถานการณ์เหล่านี้ เมื่อคุณใช้DispatchGroup()บางครั้งอาจเกิดการหยุดชะงัก

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!)
        }
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.