我是Swift并发的新手,试图理解Task -> ChildTask的关系。
我在这里创建了两个任务
- 父任务:
- (1)从MainThread调用Actor中的test()方法。
- (3.2)Actor在后台线程中调用test()方法.
- (2)然后将控制返回到主线程上的父任务
- 子任务:
- (3)在MainThread中调用
class MyViewController: ViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
print("1 \(Thread.current.isMainThread)")
Task {
async let _ = ViewModel().test() // Throws 3.3 CancellationError()
// await ViewModel().test() // Does not cancel task and works fine
print("2 \(Thread.current.isMainThread)")
Task {
do {
Thread.sleep(forTimeInterval: 0.5)
print("4.1 \(Thread.current.isMainThread)")
} catch {
print("4.2 \(Thread.current.isMainThread)")
print(error)
}
}
}
}
actor ViewModel {
func test() async {
print("3.1 \(Thread.current.isMainThread)")
do {
print("3.2 \(Thread.current.isMainThread)")
let images = try await downloadImage()
} catch {
print("3.3 \(error)")
}
}
func downloadImage() async throws -> UIImage {
try Task.checkCancellation()
await Thread.sleep(forTimeInterval: 1) // 1seconds
return UIImage()
}
}
}
async let _ = ViewModel().test() // Throws 3.3 CancellationError()
// await ViewModel().test()
1 true
2 true
3.1 false
3.2 false
3.3 CancellationError()
4.1 true
// async let _ = ViewModel().test()
await ViewModel().test() // Does not cancel task and works
1 true
3.1 false
3.2 false
2 true
4.1 true
- 我这里的问题是,为什么任务被取消时,我不等待*c_let test()方法
3条答案
按热度按时间093gszye1#
你不应该在
async
上下文中使用Thread.current
或Thread.sleep
。这在Swift 6中将是一个错误。让我们删除它们,只考虑以下代码:个字符
你似乎认为等待0.5秒的任务是运行
ViewModel().test()
的任务的“子”。这是不正确的。通过使用Task { ... }
,你启动了一个 * 顶级任务 *,它不是任何东西的子任务。你没有await
这个任务,所以外部Task
所做的就是启动ViewModel().test()
的任务,启动另一个顶级任务(不等待它),然后结束。要真正等待顶级任务,您可以执行以下操作:
型
但如果你只想等待一段时间,你根本不需要一个顶级任务。直接写就行了:
型
现在“
Task.sleep
“任务是您创建的单个顶层任务的子任务。我假设您从现在开始进行了上述更改。不像
await
实际上是等待,async let
是与它周围的代码并行运行的。请参阅Swift指南中的这一节以获得示例。如果你在某个时候不await
let
创建的变量(你甚至没有给予名称),没有人会等待它完成。型
所以没有人会等待
ViewModel().test()
完成。在并行启动ViewModel().test()
后,您只需要等待0.5秒,这不足以让ViewModel().test()
完成。0.5秒后,顶层任务结束,运行ViewModel().test()
的任务被取消,因为它是顶层任务的子任务。这解释了CancellationError
。taor4pac2#
大家问:
我的问题是,为什么我没有等待async_let test()方法,任务就被取消了
请参阅SE-0317 - async let - implicit async let waiting,其中指出如果您无法执行
await
,则会“隐含地取消并等待”。请考虑:
字符串
如果你运行这个,因为
Task.sleep
是非阻塞的,并且处理取消,所以一旦x1m2 n1福尔斯范围,foo
就会被立即取消,它也会被等待,但是因为取消是立即处理的,所以很难观察到。所以,让我们考虑这个例子:
型
现在,在本例中,您将再次看到隐式取消,并等待
foo
和bar
。(即,不立即处理取消,而是仅在Thread.sleep
完成之后检测到取消,并且我们手动地进行checkCancellation
),go
函数将await
完成bar
。“但是,如果我们将
bar
更改为主动响应取消,即改用Task.sleep
(如foo
),那么现在两者都将被快速取消并等待。bis0qfac3#
正如@Sweeper所说,父任务不会等待子任务完成,它们只共享任务的上下文(无论是要取消的任务还是启动它的线程)
我在任务中添加了一些
defer {logs}
,发现父任务在取消之前被释放了prints 5.x
。因此,task_let_task也被取消了.字符串