Блок уведомлений DispatchGroup вызывается досрочно - PullRequest
0 голосов
/ 25 января 2019

Возникла проблема с тем, что блок уведомлений DispatchGroup вызывался в моем приложении на ранней стадии, и этот пример игровой площадки стал экспериментом. Судя по выводу, иногда он вызывается еще до первого .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 голосов
/ 25 января 2019

Ваша основная проблема заключается в том, что вы звоните dg.enter() не в том месте.Вы всегда хотите вызвать enter() перед асинхронным вызовом и хотите позвонить leave(), когда асинхронный вызов завершен.

Поскольку ваш код написан сейчас, цикл for завершается до первого вызоваenter когда-либо установлен, поэтому notify запускается немедленно.

func doWork(attemptNo: Int, who: String, secs: TimeInterval) -> Void {
    dg.enter()
    DispatchQueue.global().async {
        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()
    }
}

Ваш код также странным образом использует семафор и сон.Я предполагаю, что это попытка симулировать длительный фоновый процесс.

0 голосов
/ 25 января 2019

Это просто, 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()
        }
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...