Swift Concurrency框架之Task和TaskGro

2023-02-22  本文已影响0人  zzzworm

文章序列:

Task

在swift中创建Task很简单,使用Task类的构造函数就可以

let basicTask = Task {
    let result = await someAsync()
    return result
}

Task可以返回执行结果,当任务执行出错时也可以抛出异常

let basicTask = Task {
    // .. perform some work
    throw ExampleError.somethingIsWrong
}

do {
    print(try await basicTask.value)
} catch {
    print("Basic task failed with error: \(error)")
}

我们看一下Task的定义会发现创建Task实际上是通过构造一个闭包来初始化了一个Task实例,在这个闭包里可以执行任意异步函数。

public init(
    priority: _Concurrency.TaskPriority? = nil, 
    operation: @escaping  () async -> Success)

public init(
    priority: _Concurrency.TaskPriority? = nil, 
    operation: @escaping  () async throws -> Success)

除了 init 构造 Task 之外,Task 还提供 detach 函数来创建一个不一样的 Task:

public static func detached(
    priority: _Concurrency.TaskPriority? = nil, 
    operation: @escaping  () async -> Success
) -> _Concurrency.Task<Success, Failure>

public static func detached(
    priority: _Concurrency.TaskPriority? = nil, 
    operation: @escaping  () async throws -> Success
) -> _Concurrency.Task<Success, Failure>

Task.detached 创建的task和 Task.init 创建的Task有所不同的是 Task.detached 创建的task独立运行于当前的Actor,这个Actor的概念我们以后会讲到,你可以先简单理解为线程执行的隔离上下文(保障线程安全用的)。
Task的取消很简单,调用task的cancel方法就行

let imageTask = Task { () -> UIImage? in
        let imageURL = URL(string: "https://source.unsplash.com/random")!
        let (imageData, _) = try await URLSession.shared.data(from: imageURL)
        return UIImage(data: imageData)
    }
    // Cancel the image request right away:
    imageTask.cancel()

非结构化任务和结构化任务

通过Task创建构造的任务,也被称为非结构化的并发任务。与之对应的是添加到TaskGroup的任务被称为结构化Task。
TaskGroup可以通过add方法将一系列Task添加到一个组里面,TaskGroup会等待所有task都返回后才进行返回。

let images = await withTaskGroup(of: UIImage.self, returning: [UIImage].self) { taskGroup in
    let photoURLs = await listPhotoURLs(inGallery: "Amsterdam Holiday")
    for photoURL in photoURLs {
        taskGroup.addTask { await downloadPhoto(url: photoURL) }
    }

    var images = [UIImage]()
    for await result in taskGroup {
        images.append(result)
    }
    return images
}

TaskGroup 也遵循AsyncSequence协议,可以使用reduce方式获取结果

let images = try await withThrowingTaskGroup(of: UIImage.self, returning: [UIImage].self) { taskGroup in
    let photoURLs = try await listPhotoURLs(inGallery: "Amsterdam Holiday")
    for photoURL in photoURLs {
        taskGroup.addTask { try await downloadPhoto(url: photoURL) }
    }

    return try await taskGroup.reduce(into: [UIImage]()) { partialResult, image in
        partialResult.append(image)
    }
}

上述例子中的TaskGroup会等待每个任务都执行完成,如果遇到一个任务失败想要提前返回,也可以提前return或者针对ThrowingTaskGroup抛出异常

let images = try await withThrowingTaskGroup(of: UIImage.self, returning: [UIImage].self) { taskGroup in
    let photoURLs = try await listPhotoURLs(inGallery: "Amsterdam Holiday")
    for photoURL in photoURLs {
        taskGroup.addTask { try await downloadPhoto(url: photoURL) }
    }

    var images = [UIImage]()

    /// Note the use of `next()`:
    while let downloadImage = try await taskGroup.next() {
        images.append(downloadImage)
    }
    return images
}

这里调用next方法在结果返回为nil时结束了循环,提前return之前接收的结果集。

如果要对TaskGroup进行取消,可以调用cancelAll()对添加到组里的每个Task进行取消操作。需要注意的是TaskGroup调用取消操作后,后续添加的task不会自动开始而是自动取消,如果还需要任务进行可以通过addTaskUnlessCancelled()来让任务执行。

上一篇下一篇

猜你喜欢

热点阅读