DispatchGroup通知块被提前调用

时间:2019-01-25 14:33:55

标签: swift asynchronous grand-central-dispatch semaphore

在我的应用程序中早期调用了DispatchGroup的notify块时出现问题,并使该示例成为实验示例。根据输出,有时甚至在第一个.leave()之前调用它。感觉好像我遗漏了一些明显的东西,而现在我看了太久了。

let s = DispatchSemaphore(value: 1)
let dg = DispatchGroup()
func go() -> Void {
    for i in 1...2 {
        doWork(attemptNo: i, who: "Lily", secs: Double.random(in: 1.0...5.0))
        doWork(attemptNo: i, who: "Emmie", secs: Double.random(in: 1.0...10.0))
        doWork(attemptNo: i, who: "Wiley", secs: Double.random(in: 1.0...3.0))
    }
}
func doWork(attemptNo: Int, who: String, secs: TimeInterval) -> Void {
    DispatchQueue.global().async {
        dg.enter()
        print("\(who) wants to work, will wait \(secs) seconds, attempt #\(attemptNo)")
        if s.wait(timeout: .now() + secs) == .timedOut {
            print("\(who) was denied. No soup for me! Task #\(attemptNo) not going to happen.")
            dg.leave()
            return
        }
        let workSecs = UInt32(Int.random(in: 1...3))
        print("\(who) went to work for \(workSecs) seconds on task #\(attemptNo)")

        DispatchQueue.global().asyncAfter(deadline: .now() + TimeInterval(workSecs)) {
            print("\(who) is sliding down the dinosaur tail. Task #\(attemptNo) all done!")
            s.signal()
            dg.leave()
       }
   }
}
go()
dg.notify(queue: .global(), execute: {print("Everyone is done.")})

示例输出:

Emmie wants to work, will wait 4.674405654828654 seconds, attempt #1
Lily wants to work, will wait 1.5898288206500877 seconds, attempt #1
Wiley wants to work, will wait 1.2182416407288 seconds, attempt #1
Lily wants to work, will wait 3.3225083978280647 seconds, attempt #2
Everyone is done.
Wiley wants to work, will wait 2.801577828588925 seconds, attempt #2
Emmie wants to work, will wait 8.9696422949966 seconds, attempt #2
Lily went to work for 3 seconds on task #2
Wiley was denied. No soup for me! Task #1 not going to happen.
Lily was denied. No soup for me! Task #1 not going to happen.
Wiley was denied. No soup for me! Task #2 not going to happen.
Lily is sliding down the dinosaur tail. Task #2 all done!
Emmie went to work for 3 seconds on task #1
Emmie is sliding down the dinosaur tail. Task #1 all done!
Emmie went to work for 2 seconds on task #2
Emmie is sliding down the dinosaur tail. Task #2 all done!

在这种情况下,“每个人都完成了”几乎立即发生,并且由于.leave要么没有获得信号灯,要么在完成“工作”之后就没有意义了。请帮助。

2 个答案:

答案 0 :(得分:2)

您的主要问题是在错误的位置致电mysql -h localhost -u root -p Enter password: Welcome to the MariaDB monitor. Commands end with ; or \g. Your MariaDB connection id is 10 Server version: 10.3.12-MariaDB Homebrew Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others. USE my_old_database; Database changed MariaDB [my_old_database]> 。您总是想在异步调用之前先调用dg.enter(),而在异步调用结束后又要调用enter()

现在编写代码时,leave()循环在第一次调用for之前结束,这就是立即触发enter的原因。

notify

您的代码还怪异地使用了信号灯和睡眠。我想这是模拟长时间运行的后台进程的尝试。

答案 1 :(得分:0)

这很简单,doWork在进入组之前返回...

这应该可以正常工作

import Foundation

let s = DispatchSemaphore(value: 1)
let dg = DispatchGroup()
func go() -> Void {
    for i in 1...2 {
        doWork(attemptNo: i, who: "Lily", secs: Double.random(in: 1.0...5.0))
        doWork(attemptNo: i, who: "Emmie", secs: Double.random(in: 1.0...10.0))
        doWork(attemptNo: i, who: "Wiley", secs: Double.random(in: 1.0...3.0))
    }
}
func doWork(attemptNo: Int, who: String, secs: TimeInterval) -> Void {
    dg.enter()
    DispatchQueue.global().async {
        //dg.enter()
        print("\(who) wants to work, will wait \(secs) seconds, attempt #\(attemptNo)")
        if s.wait(timeout: .now() + secs) == .timedOut {
            print("\(who) was denied. No soup for me! Task #\(attemptNo) not going to happen.")
            dg.leave()
            return
        }
        let workSecs = UInt32(Int.random(in: 1...3))
        print("\(who) went to work for \(workSecs) seconds on task #\(attemptNo)")
        sleep(workSecs)
        print("\(who) is sliding down the dinosaur tail. Task #\(attemptNo) all done!")
        s.signal()
        dg.leave()
    }
}
go()
dg.notify(queue: .global(), execute: {print("Everyone is done.")})

避免此类错误的最佳方法是使用适当的API

    func doWork(attemptNo: Int, who: String, secs: TimeInterval) -> Void {
        DispatchQueue.global().async(group: dg) {
            print("\(who) wants to work, will wait \(secs) seconds, attempt #\(attemptNo)")
            if s.wait(timeout: .now() + secs) == .timedOut {
                print("\(who) was denied. No soup for me! Task #\(attemptNo) not going to happen.")
                return
            }
            let workSecs = UInt32(Int.random(in: 1...3))
            print("\(who) went to work for \(workSecs) seconds on task #\(attemptNo)")
            sleep(workSecs)
            print("\(who) is sliding down the dinosaur tail. Task #\(attemptNo) all done!")
            s.signal()
        }
    }