当同一组中的另一个任务先前失败时,任务组中的任务不会收到取消状态

2024-03-10

我发现当任务组中的任何任务因错误而失败时,任务取消状态不会传播。

在我的示例中,两个长时间运行的异步操作同时启动。这1st一个持续3sec并失败了。这2nd一个持续6sec并成功完成。两项任务均检查Task.isCancelled完成前的状态。实际上我看到6s任务没有收到Task.isCancelled state.

我的期望是尽快1st任务失败然后任务组发送cancel组中所有剩余的任务。所以 6 秒任务应该通过检查Task.isCancelled

在长时间运行的异步操作的源代码下面,它通过以下方式模拟长时间运行的执行sleep(). After sleep()它检查Task.isCancelled state:

func runLongOperation(key: String, sleepSec: UInt32, shouldFailWithError: Bool) async throws -> String {
    try await withUnsafeThrowingContinuation { continuation in
        let queue = DispatchQueue(label: "loq")
        queue.async {
            print(" \(key) started...")
            sleep(sleepSec)
            
            if Task.isCancelled {
                print(" ...\(key) Task is cancelled")
            }
                
            if shouldFailWithError {
                print(" ...\(key) finished with failure")
                continuation.resume(throwing: MyError.generic)
            }
            else {
                print(" ...\(key) finished with success")
                continuation.resume(returning: key)
            }
        }
    }
}

此异步函数在任务组中同时执行 2 个长时间运行的操作:

func runTasksGroup() async throws -> [String] {
    return try await withThrowingTaskGroup(of: String.self, body: { group in
        group.addTask {
            return try await self.runLongOperation(key: "#1[3s]", sleepSec: 3, shouldFailWithError: true)
        }
        group.addTask {
            return try await self.runLongOperation(key: "#2[6s]", sleepSec: 6, shouldFailWithError: false)
        }
        
        var strs = [String]()
        for try await str in group {
            strs += [str]
        }
        return strs
    })
}

And runTasksGroup函数从同步开始viewDidLoad()功能

override func viewDidLoad() {
    super.viewDidLoad()
    Task {
        do {
            let strs = try await runTasksGroup()
            print("all done with success \(strs.joined(separator: " "))")
        }
        catch {
            print("some tasks failed")
        }
    }
}

输出

 #1[3s] started...
 #2[6s] started...
 ...#1[3s] finished with failure
 ...#2[6s] finished with success
some tasks failed

持续6秒的任务没有收到Task.isCancelled状态,虽然 3sec 任务之前已经失败了。 谁能解释一下为什么6s任务没有取消?


The sleep in runLongOperation不会响应取消。并且调度到调度队列的块不会自动捕获取消。

  • 理想情况下,如果块中的代码可以重构为不异步分派到 GCD 队列,则可以正确处理取消。例如,考虑使用可取消的Task.sleep(nanoseconds:) https://developer.apple.com/documentation/swift/task/sleep(nanoseconds:):

    func runLongOperation(key: String, sleepSec: UInt64, shouldFailWithError: Bool) async throws -> String {
        logger.debug("\(key) started...")
        try await Task.sleep(nanoseconds: sleepSec * NSEC_PER_SEC)
    
        if shouldFailWithError {
            logger.error("\(key) ... finished with failure")
            throw MyError.generic
        } else {
            logger.debug("\(key) ... finished with success")
            return key
        }
    }
    

    或者,如果您想记录取消,则捕获、打印并重新抛出错误:

    func runLongOperation(key: String, sleepSec: UInt64, shouldFailWithError: Bool) async throws -> String {
        do {
            logger.debug("\(key) started...")
            try await Task.sleep(nanoseconds: sleepSec * NSEC_PER_SEC)
    
            if shouldFailWithError {
                logger.error("\(key) ... finished with failure")
                throw MyError.generic
            } else {
                logger.debug("\(key) ... finished with success")
                return key
            }
        } catch {
            logger.error("\(key): \(error.localizedDescription)")
            throw error
        }
    }
    
  • 如果您正在使用延续(例如,将调度包装到调度队列内withCheckedThrowingContinuation,如您的示例中所示),您需要手动处理取消。如果您正在包装一些遗留的、可取消的异步流程,您将使用withTaskCancellationHandler。如果您正在循环(例如,进行一些长时间计算),您可以使用以下任一方法手动测试取消状态Task.checkCancellation或定期测试Task.isCancelled. But sleep是不可取消的。

    让我们假设您的任务实际上是一个缓慢、阻塞、不可取消的过程。因此,有一些观察结果:

    1. 使用调度队列会让你脱离可以使用的上下文Task.isCancelled不再了。

    2. 如果可能的话,我鼓励您留在 Swift 并发协作线程池中。即,使用分离任务而不是 GCD。

    3. 您可以使用withTaskCancellationHandler检测取消并更新您自己的状态。

    4. 您将需要同步对该状态变量的访问。要么创建自己的Sendable具有某种同步的状态属性,或者使用参与者。

    例如,下面我正在运行一个缓慢的、不可取消的进程,并执行一个独立的任务:

    func runLongOperation(key: String, sleepSec: UInt32, shouldFailWithError: Bool) async throws -> String {
        let state = State()
    
        return try await withTaskCancellationHandler {
            try await withCheckedThrowingContinuation { continuation in
                Task.detached {
                    print(" \(key) started...")
                    sleep(sleepSec)
    
                    if await state.isCancelled {
                        print(" ...\(key) Task is cancelled")
                        continuation.resume(throwing: CancellationError())
                    } else if shouldFailWithError {
                        print(" ...\(key) finished with failure")
                        continuation.resume(throwing: MyError.generic)
                    } else {
                        print(" ...\(key) finished with success")
                        continuation.resume(returning: key)
                    }
                }
            }
        } onCancel: {
            Task { await state.cancel() }
        }
    }
    

    Where State是线程安全类型:

    actor State {
        private(set) var isCancelled = false
    
        func cancel() {
            isCancelled = true
        }
    }
    
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

当同一组中的另一个任务先前失败时,任务组中的任务不会收到取消状态 的相关文章

  • 我的结构不符合协议“Decodable”/“Encodable”

    我试图使用 Codable 来保存我正在创建的应用程序中的数据 但是当我将 Codable 放入我的结构中时 我不断收到错误 类型 ReminderGroups 不符合协议 Decodable and 类型 ReminderGroups 不
  • 解包可选值时意外发现 nil - 使用 ALAMOFIRE

    我正在尝试使用 Alamofire 获取 JSON 格式的数据 当我使用一个 URL 时 它工作正常 当我使用另一个 URL 时 我在解包可选值时收到错误 我似乎无法追踪错误来自哪里 我已采取将代码放入 ViewDidLoad 来跟踪错误
  • 从 Firestore Swift 获取文档 ID

    我正在尝试从中获取文档IDFirestore通过执行这样的查询 func updateStatusInFirestore let orderid saleOrder first Orderid print orderid let setti
  • Swift - 在 TableView 单元格中使用步进器递增标签

    这里又是一个 Swift 初学者 我只是想在每个 TableView 单元格中使用一个步进器来增加同一单元格中的标签 我发现了关于这个主题的几个问题 但它们包含其他元素 我无法提取基本概念 Swift Stepper Action 更改同一
  • 如何读取 Xcode 6.1 Instruments .trace 文件?

    我一直在尝试阅读 trace文件 我使用生成的custom仪器模板 仪器 自动机 分配 Leaks using 仪器 https developer apple com library mac documentation Developer
  • 使用 Swift 创建 NSAlert

    我有在 Objective C 中创建和 NSAlert 的代码 但我现在想在 Swift 中创建它 该警报旨在确认用户想要删除文档 我想要 删除 按钮来运行删除功能 而 取消 按钮只是为了消除警报 我怎样才能用 Swift 写这个 NSA
  • ExpandableLabel iOS 中的“少看”

    我正在使用第三方库可扩展标签 https github com apploft ExpandableLabel实施一个see more特征 我正在寻找仅快速的解决方案 其中包含标签中的文本而不是按钮中的文本 因此这可以完美地工作 添加库并更
  • 在completionHandlers中存储值 - Swift

    我正在创建一个completionHandler它返回一个字典 但是当我在另一个类中调用这个方法时 它的值是零 func fetchLatestPosts completionHandler responseDict NSDictionar
  • ArraySlice 中的 Swift [重复]

    这个问题在这里已经有答案了 在数组上使用 prefix 方法后 我得到了所谓的 arraySlice 我怎样才能将其转换为数组 我试图从 FacebookGraphApi 获取 Ints 然后请求前 3 个 前缀 3 并尝试将它们添加到新数
  • 在 Swift 中使用 commitEditingStyle 动态删除 UITable 部分

    我正在处理一个无法解决的问题 我有一个来自客户数据库数组的名称表 每个客户在其他数据成员中都有一个名称属性 我可以成功删除某个部分中的行 但我不能删除该部分 当该部分中的最后一行被删除时 该部分必须消失 I got NSInternalIn
  • 从 Firestore 获取值并使用异步将输出存储为全局

    我正在尝试获取 firestore 数据 然后将其存储在变量中 async function getchildContent Parent Message let count 0 var db firebase firestore var
  • 使用prepareForSegue传递数据

    我试图将数据从viewController 1传递到viewController2 我有2个按钮和1个segue 因此有一个segue标识符 这2个按钮 按下时每个按钮应显示 1个标签用于显示标题 1个textView用于显示定义 我很难显
  • 保存来自 TrueDepth 相机的深度图像

    我正在尝试保存 iPhone X TrueDepth 相机的深度图像 使用AVCam照片滤镜 https developer apple com library content samplecode AVCamPhotoFilter Lis
  • Swift:检查 UISearchBar.text 是否包含 url

    如何检查 UISearchBar text 是否包含 URL 我想做这样的事情 if searchBar text NSTextCheckingType Link 但我收到错误 String is not convertible to NS
  • iOS Swift 在后台下载大量小文件

    在我的应用程序中 我需要下载具有以下要求的文件 下载大量 例如 3000 个 小 PNG 文件 例如 5KB 逐个 如果应用程序在后台继续下载 如果图像下载失败 通常是因为互联网连接丢失 请等待 X 秒然后重试 如果失败Y次 则认为下载失败
  • 针对 iOS 10.3 进行编译,但模块“SwiftUICharts”的最低部署目标为 iOS 13.0

    知道如何仅在 iOS 版本超过 iOS 13 时导入 SwiftUICharts 框架吗 我通过 文件 gt Swift 包 gt 添加包依赖项 添加了此框架 我的应用程序目标必须是 iOS 10 我将此框架导入到 swiftui 控制器中
  • 在 UITableViewController 中重新排序行后 UI 更新不正确

    因此 我对表中的行重新排序 用户界面最终结果不正确 场景如下 表内容原文 a b c d e 如果我移动第 0 行 当前a 到第 4 行 当前e 我看到的最终结果是 c d e a a 一些背景 该表正在读取 Realm 对象的列表 我确认
  • 带约束的嵌套集合视图的意外行为 (Swift 4)

    我的表格视图中有一个单元格 其中包含水平分页集合视图 该集合视图的每个页面内都有一个垂直集合视图 为了避免 滚动滚动 问题 我在垂直集合视图中禁用了垂直滚动 垂直集合视图的单元格计数不是静态的 可以是任意数字 因此 这会产生一个问题 集合视
  • NSURLCache 不缓存

    我正在使用 Xcode 6 1 6A1030 iOS7 和 iOS8 模拟器 NSURLCache 似乎没有缓存任何东西 我使用 Cache Control 标头 我的服务器返回带有 max age 6000 的 Cache Control
  • iOS Swift 和 reloadRowsAtIndexPaths 编译错误

    我与 xCode Swift 陷入僵局并刷新 UITableView 的单行 这条线有效 self tableView reloadData 而这条线没有 self tableView reloadRowsAtIndexPaths curr

随机推荐

  • Spring - 从查询中获取结果集

    我想用Spring JDBCTemplate但我想收到ResultSet 它不会将完整的查询结果存储在内存中 就像您会发现使用 java 执行标准语句一样JDBC 我发现的最接近的ResultSet was SqlRowSet sqlRow
  • 使用 SimpleDateFormat 时出错

    我正在尝试使用 SimpleDateFormat 类从该字符串中解析 DateTime 2012 年 7 月 5 日 11 38 02 442 世界标准时间 UTC 上午 我尝试了以下格式字符串 SimpleDateFormat datef
  • 数据库设计(库存数据库)

    我正在寻求设计一个跟踪小吃店的库存数据库 由于这将是单人 计算机访问 并且需要轻松移动到另一个系统 因此我计划使用 SQLite 作为数据库引擎 基本概念是跟踪从 Sams Club 等批发仓库购买的库存 然后跟踪库存 我试图克服的主要障碍
  • 在 WooCommerce 3 中获取订单运送商品详细信息

    我怎样才能得到订单运输方式 ID 例如 flate rate 自 WooCommerce 3 以来 一切都发生了变化 现在变得很复杂 我已经尝试过 order gt get data 在 foreach 循环中 但数据受到保护 如果您想获取
  • R 将整个文件夹移动到另一个目录

    我想将整个文件夹从一个目录移动到另一个目录 这是我的代码 folder old path C Users abc Downloads managerA path new C User abc Desktop managerA current
  • 一张数据库表可以包含多个主键吗?

    一张数据库表可以包含多个主键吗 是的 我说的是 RDBMS 一张表可以有 没有主键 一个主键由一列组成 或者 一个复合主键由两列或多列组成 除此之外 您可以拥有任意数量的唯一索引 这基本上可以完成相同的操作
  • 具有混合数据类型的 TensorFlow 数据集生成器

    我正在使用 TensorFlow 数据集 API https www tensorflow org guide datasets https www tensorflow org guide datasets 特别是 我将它与 Tensor
  • 在 Rails 中创建所见即所得表单生成器 (á la Wufoo)

    我必须向 Rails Web 应用程序添加类似 Wufoo 的 WYSIWYG 表单构建器功能 有谁知道有帮助的好资源 宝石 引擎 插件 示例代码 这并不是您问题的真正答案 但不幸的是 由于我的声誉水平 我仍然无法添加评论 抱歉 Drupa
  • 静态方法与否?

    我需要使用 PHP 开发一个小型 CMS 现在我正在尝试弄清楚其结构 CMS 将使用一组函数生成 诸如数据库功能 缓存 国际化之类的东西 我想这样做 使函数成为大 站点 类的非静态方法的一部分 这样我就可以运行该类的多个实例 但不确定我是否
  • 并行应用程序具有随机行为

    我正在编写一个 C 程序 使用 pthreads 在二维矩阵上进行波前模式计算 为了获得良好的性能 我以交错的方式将几行分配给每个线程 如下所示 线程0 线程 1 线程 2 线程 3 线程0 线程 1 线程 2 线程 3 etc 在这个计算
  • 当按下 alt+tab 或 windows+d 时,如何在 jquery 中触发事件?

    我想在按下 alt tab 或 windows d 时触发一个事件 以下是我的代码 当鼠标指针远离浏览器窗口时发出警报 但即使用户按 alt tab 或 Windows D 也应该发生此事件 有人可以帮我解决这方面的问题吗 以下是我的代码供
  • 在 ASP.NET Core 中获取浏览器语言?

    我试图从浏览器获取默认语言 并使用以下代码来获取它 var languages HttpContext Request UserLanguages 由于 NET Core 2 不支持上述内容 我进行了测试 var requestContex
  • Visual Studio:如何以编程方式检查使用的 C++ 平台工具集

    我必须使用 MSVC2012 和 v100 平台工具集 来自 MSVC2010 构建项目 不幸的是我正在使用 C 11 功能 范围基于 跨越代码 我想知道是否有一个预处理器指令可以在编译时了解当前的平台工具集 即 if MSC PLATFO
  • 过滤数据库表客户端 T-SQL select from case when then where 的任何列

    我希望能够过滤数据网格的列 但不知道如何修复 select 语句 这是我所能得到的 SELECT ClientID FirstName LastName BirthDate StreetName City State ZipCode Cel
  • 使用 JavaScript 和 Canvas 实现 ColorPicker

    I m trying to implement ColorPicker using Canvas just for fun But i seem lost as my browser is freezing for a while when
  • 记住在客户端独立应用程序中输入的值

    我们有一个独立的 java swing 应用程序 用户可以通过提供打印机的 IP 在打印机上打印他画的东西 现在的要求是应用程序需要记住该用户上次给出的ip 到目前为止我能想到的是 尽管是一个残酷的 在客户端计算机上保留日志文件类型的存储
  • 在 os.listdir(path) 中使用文件扩展名通配符

    我有一个正在尝试使用 Python 解析的文件目录 如果它们都是相同的扩展名 我不会有问题 但无论出于何种原因 它们都是在原始扩展名之后使用顺序数字扩展名创建的 例如 foo log foo log 1 foo log 2 bar log
  • 从接收到的数据存储过程填充自定义 C# 对象

    public class User public string FirstName get set public string LastName get set public class Address public string City
  • 未找到扩展 CordovaActivity 的 Java 文件。当使用“cordova构建”时

    我的电脑是Windows 8 64位 我为我的项目安装了cordova和android平台 我已经通过输入创建了我的项目cordova create hello com example hello HelloWorld and cordov
  • 当同一组中的另一个任务先前失败时,任务组中的任务不会收到取消状态

    我发现当任务组中的任何任务因错误而失败时 任务取消状态不会传播 在我的示例中 两个长时间运行的异步操作同时启动 这1st一个持续3sec并失败了 这2nd一个持续6sec并成功完成 两项任务均检查Task isCancelled完成前的状态