0

我对取消的任务感到有些困惑。

概述:

  • checkCancellation函数有 2 个子任务,一个任务运行computeA,另一个computeB. 它们同时运行,computeB引发错误。

怀疑:

  • 我预计子任务computeA会被取消,因为computeB引发了错误,但从computeA未被取消。
  • 我的理解是错误的还是我遗漏了什么?
  • 或者这是一个错误?

笔记:

  • 我正在使用一个SwiftUI项目(因为 Swift Playgrounds 不支持async let
  • macOS Big Sur 11.5.2 (20G95)
  • Xcode Version 13.0 beta 5 (13A5212g)

输出:

A - started
B - going to throw
A - going to return, Task.isCancelled = false
error: infinity

并发函数定义:

import Foundation
import UIKit

enum ComputationError: Error {
    case infinity
}

fileprivate func computeA() async throws -> Int {
    print("A - started")
    await Task.sleep(2 * 100_000_000)
    print("A - going to return, Task.isCancelled = \(Task.isCancelled)") //I expected Task.isCancelled to be true
    return 25
}

fileprivate func computeB() async throws -> Int {
    print("B - going to throw")
    throw ComputationError.infinity
}

func checkCancellation() async throws {
    async let a = computeA()
    async let b = computeB()
    
    let c = try await a + b
    print("c = \(c)")
}

调用并发函数

struct ContentView: View {
    var body: some View {
        Button("check cancellation") {
            Task {
                do {
                    try await checkCancellation()
                    print("normal exit")
                } catch {
                    print("error: \(error)")
                }
            }
        }
    }
}

观察:

  • 当我将代码更改为let c = try await b + a

输出:

A - started
B - going to throw
A - going to return, Task.isCancelled = true
error: infinity

怀疑:

我仍然不确定我是否理解原始代码中这种行为的原因

4

1 回答 1

2

我希望子任务 computeA 被取消,因为 computeB 抛出错误,但 computeA 从未被取消。我的理解是错误的还是我遗漏了什么?

是的,是的。你的理解是错误的,你错过了一些东西。

视频中的备注与任务组中创建的子任务有关。实际发生的是,当错误从子任务渗透到任务组时,任务组会调用cancel所有其他子任务。

但是正在使用async let. 没有任务组。所以没有人“在那里”取消另一个子任务。事实上,如果您仔细观看该视频,您会发现它正确地告诉了您会发生什么:

  • 如果父任务被取消,子任务会自动取消。

  • 如果一个async let任务在第二个任务被初始化之前就抛出了async let,那么第二个任务就“生来就被取消”了。还在等待中(视频对此讲得很清楚,而且是正确的);如果它没有通过中止来响应取消,它将继续完成。

这里的重点是父任务必须以良好的顺序结束。在它的孩子以一种或另一种方式完成之前,它不会结束。(同样,视频对此非常清楚。)如果这意味着自动等待任务完成,尽管抛出,那么这就是将会发生的事情。

需要注意的另一件有趣的事情是async let,在您try await稍后说的地方,如果子任务抛出,您甚至不会听到它(在这方面也不会发生任何事情),直到您到达发生的地步try await。换句话说,投掷只能渗透到您实际说的点try;没有其他任何意义。

于 2021-09-07T17:27:21.087 回答