【发布时间】:2020-07-10 20:27:18
【问题描述】:
我想在 Swift Playgrounds 中测试/显示与延迟后改变值的函数相关的行为。为简单起见,假设它改变了一个字符串。我知道我可以通过DispatchQueue.main.asyncAfter 延迟更新值的执行,并且我可以使用usleep 或sleep 休眠当前线程。
但是,由于 Playground 似乎在同步线程中运行,所以我无法看到睡眠后的变化。
这是我想做的一个例子:
var string = "original"
let delayS: TimeInterval = 0.100
let delayUS: useconds_t = useconds_t(delayS * 1_000_000)
func delayedUpdate(_ value: String) {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + delayS) {
string = value
}
}
delayedUpdate("test2")
assert(string == "original")
usleep(delayUS)
print(string) // ❌ Prints "original"
assert(string == "test2") // ❌ Assertion failure. string is "original" here
delayedUpdate("test3")
assert(string == "test2") // ❌ Assertion failure. string is "original" here
usleep(delayUS)
print(string) // ❌ Prints "original"
assert(string == "test3") // ❌ Assertion failure. string is "original" here
delayedUpdate("test4")
assert(string == "test3") // ❌ Assertion failure. string is "original" here
usleep(delayUS)
print(string) // ❌ Prints "original"
assert(string == "test4") // ❌ Assertion failure. string is "original" here
请注意所有失败的断言,因为顶层的任何内容都看不到对 string 的更改。这似乎是一个同步与异步线程的问题。
我知道我可以通过将usleep 替换为更多asyncAfter 来修复它:
delayedUpdate("test2")
assert(string == "original")
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + delayS) {
print(string)
assert(string == "test2")
delayedUpdate("test3")
assert(string == "test2")
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + delayS) {
print(string)
assert(string == "test3")
delayedUpdate("test4")
assert(string == "test3")
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + delayS) {
print(string)
assert(string == "test4")
}
}
}
但是,每次应用程序延迟时,这都会导致缩进代码的金字塔。这 3 个关卡还不错,但如果我有一个大操场,这将变得非常难以遵循。
有没有办法使用更接近第一种线性编程风格的东西,尊重延迟后更新的更新?
另一个可能的解决方案是将每个对 string 的引用包装在 asyncAfter 中:
delayedUpdate("test2")
assert(string == "original")
usleep(delayUS)
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.001) { print(string) }
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.001) { assert(string == "test2") }
delayedUpdate("test3")
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.001) { assert(string == "test2") }
usleep(delayUS)
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.001) { print(string) }
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.001) { assert(string == "test3") }
delayedUpdate("test4")
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.001) { assert(string == "test3") }
usleep(delayUS)
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.001) { print(string) }
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.001) { assert(string == "test4") }
但是,这也不是首选,因为它也非常混乱,并且例如,如果一次执行依赖于 string 的先前值来执行其功能,则可能容易出错。它还需要0.001 或类似的更正,以确保不存在竞争条件。
如何在 Swift Playground 中使用线性编程风格(例如使用 sleep),但在睡眠期间更新的值会被 sleep 之后的行正确反映?
【问题讨论】:
标签: swift multithreading swift-playground