该second
任务不等待first
在单独线程上运行的任务完成。如果您在任务中执行了一些耗时的first
操作,并且您将看到second
任务根本没有等待,则可以说明这一点。
usingTask { … }
更类似于DispatchQueue.global().async { … }
to DispatchQueue.main.async { … }
。它从first
一个单独的线程开始。这引入了first
和之间的竞赛second
,您无法保证他们将按照哪个顺序运行。(在我的测试中,它大部分时间都在之前运行,但它仍然可以偶尔在second
之前运行。)first
first
second
所以,问题是,你真的关心这两个任务从哪个顺序开始吗?如果是这样,您可以通过(显然)Task { await first() }
在调用second
. 还是您只是想确保second
不会等待first
完成?在这种情况下,这已经是行为,不需要更改您的代码。
您问:
如果await first()
需要在同一个队列上second()
异步运行怎么办。......我只是在想[如果它在后台线程上运行] 将意味着由于 UI 更新而不是来自主线程而导致的崩溃。
您可以使用 标记更新 UI 的例程@MainActor
,这将导致它在主线程上运行。但请注意,不要将此限定符与耗时任务本身一起使用(因为您不想阻塞主线程),而是将耗时操作与 UI 更新解耦,并将后者标记为@MainActor
.
例如,这是一个手动异步计算 π 并在完成后更新 UI 的示例:
func startCalculation() {
Task {
let pi = await calculatePi()
updateWithResults(pi)
}
updateThatCalculationIsUnderway() // this really should go before the Task to eliminate any races, but just to illustrate that this second routine really does not wait
}
// deliberately inefficient calculation of pi
func calculatePi() async -> Double {
await Task.detached {
var value: Double = 0
var denominator: Double = 1
var sign: Double = 1
var increment: Double = 0
repeat {
increment = 4 / denominator
value += sign * 4 / denominator
denominator += 2
sign *= -1
} while increment > 0.000000001
return value
}.value
}
func updateThatCalculationIsUnderway() {
statusLabel.text = "Calculating π"
}
@MainActor
func updateWithResults(_ value: Double) {
statusLabel.text = "Done"
resultLabel.text = formatter.string(for: value)
}
注意:为了确保这种缓慢的同步计算calculatePi
不在当前参与者(可能是主要参与者)上运行,我们需要一个“非结构化任务”。具体来说,我们想要一个“分离任务”,即不在当前actor上运行的任务。正如The Swift Programming Language: Concurrency: Tasks and Task Groups的非结构化并发部分所说:
要创建在当前参与者上运行的非结构化任务,请调用Task.init(priority:operation:)
初始化程序。要创建不属于当前参与者的非结构化任务,更具体地称为分离任务,请调用Task.detached(priority:operation:)
类方法。