Tim*_*m C 4 asynchronous semaphore grand-central-dispatch swift
在我的应用程序中早期调用 DispatchGroup 的通知块时遇到问题,并使用此 Playground 示例进行实验。根据输出,有时它甚至在第一个 .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.")})
Run Code Online (Sandbox Code Playgroud)
示例输出:
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!
Run Code Online (Sandbox Code Playgroud)
在这种情况下,“每个人都完成了”几乎立即发生,并且由于 .leave 与没有获得信号量或在“工作”完成之后一起发生,这没有意义。请帮忙。
你的主要问题是你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()
}
}
Run Code Online (Sandbox Code Playgroud)
您的代码还奇怪地使用了信号量和睡眠。我想这是模拟长时间运行的后台进程的尝试。
那就简单了,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.")})
Run Code Online (Sandbox Code Playgroud)
避免此类错误的最佳方法是使用正确的 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()
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1103 次 |
| 最近记录: |