「实战指南 」Swift 并发中的任务取消机制

在这里插入图片描述

网罗开发 (小红书、快手、视频号同名)

  大家好,我是 展菲,目前在上市企业从事人工智能项目研发管理工作,平时热衷于分享各种编程领域的软硬技能知识以及前沿技术,包括iOS、前端、Harmony OS、Java、Python等方向。在移动端开发、鸿蒙开发、物联网、嵌入式、云原生、开源等领域有深厚造诣。

图书作者:《ESP32-C3 物联网工程开发实战》
图书作者:《SwiftUI 入门,进阶与实战》
超级个体:COC上海社区主理人
特约讲师:大学讲师,谷歌亚马逊分享嘉宾
科技博主:华为HDE/HDG

我的博客内容涵盖广泛,主要分享技术教程、Bug解决方案、开发工具使用、前沿科技资讯、产品评测与使用体验。我特别关注云服务产品评测、AI 产品对比、开发板性能测试以及技术报告,同时也会提供产品优缺点分析、横向对比,并分享技术沙龙与行业大会的参会体验。我的目标是为读者提供有深度、有实用价值的技术洞察与分析。

展菲:您的前沿技术领航员
👋 大家好,我是展菲!
📱 全网搜索“展菲”,即可纵览我在各大平台的知识足迹。
📣 公众号“Swift社区”,每周定时推送干货满满的技术长文,从新兴框架的剖析到运维实战的复盘,助您技术进阶之路畅通无阻。
💬 微信端添加好友“fzhanfei”,与我直接交流,不管是项目瓶颈的求助,还是行业趋势的探讨,随时畅所欲言。
📅 最新动态:2025 年 3 月 17 日
快来加入技术社区,一起挖掘技术的无限潜能,携手迈向数字化新征程!


文章目录

    • 前言
    • 什么是协作式取消?
    • 如何用 Task API 处理任务取消
    • 在异步方法中正确处理取消
    • 在多个步骤中检查取消状态
    • 用 isCancelled 进行检查
    • 手动取消任务
    • 总结

前言

Swift 并发提供了一种协作式取消(cooperative cancellation) 机制,来让任务在需要时自己退出。简单来说,Swift 不会强行终止你的任务,但它会告诉你任务已经被标记为取消,至于你要不要停下来,那是你自己的决定。

这篇文章会讲清楚任务取消的原理、如何正确使用它,以及如何写出高效又优雅的代码

什么是协作式取消?

协作式取消的核心思想是:

  1. 调用方(比如 SwiftUI)没法直接终止任务,只能“标记”为取消
  2. 任务本身需要定期检查这个标记,并决定要不要提前终止
  3. 你可以选择直接返回、提供部分结果,或者继续执行,全看你的业务逻辑。

简单来说,Swift 只是给你一个“信号”——“嘿,这个任务已经没用了,看看你要不要停下来”。

如何用 Task API 处理任务取消

来看个例子,这是一个 SwiftUI 界面,用户输入搜索内容时,会触发异步搜索。

struct ContentView: View {@State private var store = Store()@State private var query = ""var body: some View {NavigationStack {List(store.results, id: \.self) { result inText(verbatim: result)}.searchable(text: $query).task(id: query) {await store.search(matching: query)}}}
}

这里最关键的是 task(id: query) 这行代码:

  • query 变化时,SwiftUI 会启动一个新的搜索任务
  • 同时,它会标记上一个任务为“已取消”,但不会立刻终止它。
  • 如果旧任务里没有检查取消状态,它可能仍然会跑完所有逻辑

这意味着,如果用户输入了很多字符,可能会同时存在多个搜索任务,这就是为什么我们要手动处理取消逻辑。

在异步方法中正确处理取消

假设 Store 负责查询数据,我们的 search(matching:) 方法如下:

import HealthKit@MainActor @Observable final class Store {private(set) var results: [HKCorrelation] = []private let store = HKHealthStore()func search(matching query: String) async {let foodQuery = HKSampleQueryDescriptor(predicates: [.correlation(type: .init(.food))],sortDescriptors: [])do {let food = try await foodQuery.result(for: store)try Task.checkCancellation()  // 检查任务是否已取消results = food.filter { food inlet title = food.metadata?["title"] as? String ?? ""return title.localizedStandardContains(query)}} catch {results = []}}
}

这里有个关键点:Task.checkCancellation()

  • 这个方法会抛出一个错误,如果任务已经被取消,它就会立刻停止执行,后续的代码不会再运行。
  • 这样可以避免执行一些不必要的逻辑,比如过滤数据、更新 UI 等。
  • 如果任务被取消,我们直接把 results 置空,这样用户不会看到过时的搜索结果。

在多个步骤中检查取消状态

如果你的异步代码有多个步骤,比如先获取数据、然后再做一些处理,那你可能需要在多个关键点检查任务是否已取消,否则即使任务已经无效了,它可能还会跑完整个流程。

import HealthKit@MainActor @Observable final class Store {private(set) var results: [HKCorrelation] = []private let store = HKHealthStore()func search(matching query: String) async {let foodQuery = HKSampleQueryDescriptor(predicates: [.correlation(type: .init(.food))],sortDescriptors: [])do {let food = try await foodQuery.result(for: store)try Task.checkCancellation()  // 第一次取消检查// 假设这里有额外的数据处理try Task.checkCancellation()  // 第二次取消检查results = food.filter { food inlet title = food.metadata?["title"] as? String ?? ""return title.localizedStandardContains(query)}} catch {results = []}}
}

为什么要多次检查?

  1. 如果 foodQuery 运行了一段时间,任务被取消了,我们希望尽早停下来,而不是等所有代码都跑完。
  2. 某些任务可能是分步执行的,比如先获取原始数据,再处理数据。如果第一步完成了,但任务已经取消了,我们就没必要继续处理数据

用 isCancelled 进行检查

除了 Task.checkCancellation() 之外,Swift 还提供了 Task.isCancelled 这个属性,它是一个布尔值,你可以用它更灵活地处理任务取消:

actor SearchService {private var cachedResults: [HKCorrelation] = []private let store = HKHealthStore()func search(matching query: String) async throws -> [HKCorrelation] {guard !Task.isCancelled else {return cachedResults  // 任务取消了,直接返回缓存}let foodQuery = HKSampleQueryDescriptor(predicates: [.correlation(type: .init(.food))],sortDescriptors: [])let food = try await foodQuery.result(for: store)guard !Task.isCancelled else {return cachedResults  // 任务取消了,避免不必要的计算}cachedResults = food.filter { food inlet title = food.metadata?["title"] as? String ?? ""return title.localizedStandardContains(query)}return cachedResults}
}

两种方式的区别

  • Task.checkCancellation()如果任务已取消,直接抛出错误,代码不再继续执行
  • Task.isCancelled任务是否继续执行,由你自己决定,比如可以提前返回缓存数据,而不是直接终止。

手动取消任务

通常情况下,Swift 会帮你管理任务的取消,但如果你想手动创建和取消任务,也可以用 Task

struct ExampleView: View {@State private var store = Store()@State private var task: Task<Void, Never>?var body: some View {VStack {Button("开始任务") {task = Task {await store.fetch()}}Button("取消任务") {task?.cancel()}}}
}

这里 task?.cancel() 只会标记任务为取消,但不会真的终止它,所以你仍然需要在 fetch() 里检查 Task.isCancelledTask.checkCancellation()

总结

  1. Swift 不会自动终止任务,只会标记它为取消
  2. Task.checkCancellation() 可以立即终止任务,防止执行不必要的逻辑
  3. Task.isCancelled 可以更灵活地决定如何处理取消
  4. 如果任务有多个异步步骤,应该在关键点多次检查取消状态
  5. 手动创建的任务可以用 .cancel() 取消,但仍然需要手动检查取消状态

学会这些,你的 Swift 并发代码就能更高效、更优雅地处理任务取消,让用户体验更流畅!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/36996.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

实验12深度学习

实验12深度学习 一、实验目的 &#xff08;1&#xff09;理解并熟悉深度神经网络的工作原理&#xff1b; &#xff08;2&#xff09;熟悉常用的深度神经网络模型及其应用环境&#xff1b; &#xff08;3&#xff09;掌握Anaconda的安装和设置方法&#xff0c;进一步熟悉Jupyte…

【问题解决】Postman 测试报错 406

现象 Tomcat 日志 org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver.logException Resolved org.springframework.web.HttpMediaTypeNotAcceptableException: No acceptable representation HTTP状态 406 - 不可接收 的报错&#xff0c;核心原因 客…

微信小程序:用户拒绝小程序获取当前位置后的处理办法

【1】问题描述&#xff1a; 小程序在调用 wx.getLocation() 获取用地理位置时&#xff0c;如果用户选择拒绝授权&#xff0c;代码会直接抛出错误。如果再次调用 wx.getLocation() 时&#xff0c;就不会在弹窗询问用户是否允许授权。导致用户想要重新允许获取地理位置时&#x…

【MySQL】内置函数

目录 一、日期时间函数1.1 简单使用1.2 案例实操 二、字符串函数2.1 简单使用2.2 案例实践2.2.1 获取emp表的ename列的字符集2.2.2 要求显示exam_result表中的信息&#xff0c;显示格式&#xff1a;“XXX的语文是XXX分&#xff0c;数学XXX分&#xff0c;英语XXX分”2.2.3 求exa…

模块二 单元4 安装AD+DC

模块二 单元4 安装ADDC 两个任务&#xff1a; 1.安装AD活动目录 2.升级当前服务器为DC域控制器 安装前的准备工作&#xff1a; 确定你要操作的服务器系统&#xff08;Windows server 2022&#xff09;&#xff1b; 之前的服务器系统默认是工作组的模式workgroup模式&#xff08…

卫星互联网智慧杆:开启智能城市新时代​

哇哦&#xff01;在当下这个数字化浪潮正以雷霆万钧之势席卷全球的超酷时代&#xff0c;智慧城市建设已然成为世界各国你追我赶、竞相发力的核心重点领域啦&#xff01;而咱们的卫星互联网智慧杆&#xff0c;作为一项完美融合了卫星通信与物联网顶尖技术的创新结晶&#xff0c;…

ThreadLocal 的详细使用指南

一、ThreadLocal 核心原理 ThreadLocal 是 Java 提供的线程绑定机制&#xff0c;为每个线程维护变量的独立副本。其内部通过 ThreadLocalMap 实现&#xff0c;每个线程的 Thread 对象都有一个独立的 ThreadLocalMap&#xff0c;存储以 ThreadLocal 对象为键、线程局部变量为值…

免费开源的NAS解决方案:TrueNAS

TrueNAS是业内知名的FreeNAS系统的升级版&#xff0c;是一款开源的网络存储系统&#xff0c;具有高性能、稳定性和易用性等优点。 TrueNAS目前有三个版本&#xff0c;分别是TrueNAS CORE、TrueNAS ENTERPRISE、TrueNAS SCALE。其中&#xff0c;TrueNAS CORE基于FreeBSD开发&…

Fisher 信息矩阵公式原理:使用似然估计,二阶导数等知识点

Fisher 信息矩阵公式原理:使用似然估计,二阶导数等知识点 目录 Fisher 信息矩阵公式原理:使用似然估计,二阶导数等知识点Fisher 通过似然估计求解真实数据和权重参数之间的差异**1. Fisher 信息矩阵的定义****2. 计算对数似然函数的二阶导数****3. 代入 Fisher 信息矩阵定义…

自定义myshell(精讲)

我们都知道&#xff0c;我们给Linux下发的指令都是shell帮我们处理并完成的&#xff0c;那么他是怎么完成的呢&#xff1f;不难想到他都是通过环境变量以及程序替换来完成的。我们这一篇文章就手把手来教你怎么自己实现一个简单的shell。 目标&#xff1a; 1.要能处理普通命令 …

HTML图像标签的详细介绍

1. 常用图像格式 格式特点适用场景JPEG有损压缩&#xff0c;文件小&#xff0c;不支持透明适合照片、复杂图像PNG无损压缩&#xff0c;支持透明&#xff08;Alpha通道&#xff09;适合图标、需要透明背景的图片GIF支持动画&#xff0c;最多256色简单动画、低色彩图标WebP谷歌开…

信号的捕捉(操作部分)

目录 信号集和信号屏蔽字 信号集 信号屏蔽字 信号位操作函数 sigemptyset sigaddset sigismember sigprocmask sigpending 手动操作让2号信号屏蔽打印pending 信号处理函数sigaction 我们继续来学习信号的捕捉 信号集和信号屏蔽字 信号集 信号集是存储一组信号的…

CIR-Net:用于 RGB-D 显著性目标检测的跨模态交互与优化(问题)

摘要 问题一&#xff1a;自模态注意力优化单元和跨模态加权优化单元什么意思&#xff1f; 1 优化中间件结构的作用 位置&#xff1a;位于编码器和解码器之间 输入&#xff1a;编码器提取的RGB特征&#xff0c;深度特征以及RGB-D特征。 输出&#xff1a;经过优化的RGB&…

Linux驱动开发基础(can)

目录 1.can的介绍 2.can的硬件连接 2.1 CPU自带can控制器 2.2 CPU没有can控制器 3.电气属性 4.can的特点 5.can协议 5.1 can的种类 5.2 数据帧 5.2.1 标准数据帧格式 5.3.1 扩展数据帧格式 5.3 遥控帧 5.4 错误帧 5.5 过载帧 5.6 帧间隔 5.7 位填充 5.8 位时…

【北京迅为】iTOP-RK3568开发板OpenHarmony系统南向驱动开发UART接口运作机制

瑞芯微RK3568芯片是一款定位中高端的通用型SOC&#xff0c;采用22nm制程工艺&#xff0c;搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码&#xff0c;支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU&#xff0c;可用于轻量级人工…

【嵌入式学习】时钟 - 边缘触发锁存器

目录 ## 时钟 ## 带边缘触发的寄存器 ## 优化内存走线 ## 画16位的内存 ## 时钟 波特率&#xff1a;一分钟说几个字 clock统一计算机内部的节奏&#xff0c;clock频率越高cpu速度越快 触发&#xff1a;电压的突变&#xff1b;下降沿&#xff1a;高变低&#xff1b;上升沿…

Linux C/C++编程——线程

线程是允许应用程序并发执行多个任务的一种机制&#xff0c;线程参与系统调度。 系统调度的最小单元是线程、而并非进程。 线程包含在进程之中&#xff0c;是进程中的实际运行单位。一个线程指的是进程中一个单一顺序的控制流&#xff08;或者说是执行路线、执行流&#xff09;…

CAN通信转TCP/IP通信协议解析

背景&#xff1a;最近项目开发受限于开发版只有一路CAN口和多个CAN通信对象的帧ID一样&#xff0c;考虑采用转换模块将CAN通信转成TCP/IP通信&#xff0c;间接实现获取CAN报文数据的目的。 1. 转换模块协议 首先想到的是采购周立功他家的多路CAN通信转TCP/IP通信模块&#xf…

vue:组件的使用

Vue&#xff1a;组件的使用 1、什么是组件 1.1、传统方式开发的应用 一个网页通常包括三部分&#xff1a;结构&#xff08;HTML&#xff09;、样式&#xff08;CSS&#xff09;、交互&#xff08;JavaScript&#xff09;。在传统开发模式下&#xff0c;随着项目规模的增大&a…

强大的AI网站推荐(第一集)—— Devv AI

网站&#xff1a;Devv AI 号称&#xff1a;最懂程序员的新一代 AI 搜索引擎 博主评价&#xff1a;我的大学所有的代码都是使用它&#xff0c;极大地提升了我的学习和开发效率。 推荐指数&#xff1a;&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f;&#x…