当前位置: 首页 > news >正文

深入理解 Swift TaskGroup:从基础用法到性能优化的完整指南

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

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

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

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

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


文章目录

    • 前言
    • TaskGroup 是什么?它解决了什么问题?
    • TaskGroup 的基础用法(可运行 Demo)
      • 解析:
      • 实际场景举例:
    • 如何在 TaskGroup 中处理取消?
      • 场景举例:
    • 为什么一次性创建 1000 个子任务会导致内存暴涨?
    • 正确方式:限制并发数量,边消费边添加
    • 代码解析:为什么这样做更好?
      • 1. 一次只保持 10 个任务在内存中
      • 2. “边消费边补充”的思路就像:
      • 3. addTaskUnlessCancelled
    • 实际项目中的典型使用场景
      • 批量网络请求
      • 批量文件读取
      • 图像处理/视频处理
      • 分布式任务(结合 Swift server)
    • 总结

前言

Swift 的结构化并发(Structured Concurrency)里,TaskGroup 是一个非常强大的能力,它让我们可以更自然、更安全地并行处理一批任务,比如批量请求接口、并行读取文件、并行处理图片等。

这一篇我们就从一个可运行的例子开始,逐步讲清楚:

  • TaskGroup 的基本使用方法
  • 如何取消 TaskGroup
  • 为什么大量任务会导致内存占用暴涨
  • 如何批量并发但避免同时创建成千上万个任务
  • 如何在真实项目里落地

并且所有示例都采用 Swift Concurrency(要求 iOS 15+),如果是早期版本也可以通过 back-deploy 运行。

TaskGroup 是什么?它解决了什么问题?

在 Swift 并发之前,想要并发执行多个任务我们通常会使用:

  • DispatchGroup
  • OperationQueue
  • 手写 semaphore 配合线程池

但是这些方案都有明显缺陷,比如:

  • 逃逸闭包容易写出垃圾代码
  • 线程过多导致性能下降
  • 取消逻辑很难处理
  • 错误传递不够优雅

TaskGroup 则是结构化并发中的重要基础设施,它允许我们:

  • 动态创建任意数量的子任务
  • 等待所有任务完成
  • 优雅地处理取消
  • 任务之间隔离、无共享状态
  • 不会创建无限线程,因为使用的是协作式线程池(Cooperative Thread Pool)

一句话总结:
TaskGroup = 更现代化、更安全的并行任务管理方式

TaskGroup 的基础用法(可运行 Demo)

我们先来看一个简单的例子:并行抓取一批 URL 内容。

下面是一个可运行的 Swift 示例(你可以丢进 playground 运行):

import Foundation@MainActor
@Observable
final class Store {private(set) var messages: [String] = []func fetch(urls: [URL]) async {messages = await withTaskGroup(of: String.self,returning: [String].self) { group in// 添加子任务for url in urls {group.addTask(priority: .high) {return await self.fetch(url)}}var result: [String] = []// 等待所有任务完成for await message in group where !message.isEmpty {result.append(message)}return result}}private func fetch(_ url: URL) async -> String {// 模拟网络请求try? await Task.sleep(for: .seconds(1))return "内容来自:\(url.absoluteString)"}
}

解析:

  • withTaskGroup 用来创建任务组
  • addTask 添加子任务
  • for await 遍历所有返回结果
  • 子任务的执行是并行的

实际场景举例:

你在开发一个新闻类 App,需要同时获取多个频道的最新文章:

  • 推荐频道
  • 热门频道
  • 本地频道
  • 视频频道

这里当然可以写多条异步调用,但你更希望:

  • 所有请求是并行的(更快)
  • 任何请求失败不要影响整体
  • 能够按顺序收集结果

TaskGroup 就非常适合做这种批处理操作。

如何在 TaskGroup 中处理取消?

TaskGroup 使用的是协作式取消(Cooperative Cancellation),也就是说任务本身必须主动检查是否被取消。

例如:

group.addTask {if Task.isCancelled {return ""} else {return await fetch(url)}
}

你也可以在遍历结果时主动取消:

for await message in group {messages.append(message)// 比如只要拿到 3 条数据就不继续等了if messages.count > 3 {group.cancelAll()}
}

场景举例:

你正在做批量 OCR 识别图片。
前端用户只需要展示前 3 张结果,其余的就不需要继续识别了。

TaskGroup 可以很轻松做到:

  • 开始时并行识别 N 张
  • 得到前 3 张后马上取消剩余任务
  • 避免浪费 CPU

为什么一次性创建 1000 个子任务会导致内存暴涨?

Swift 并发使用协作式线程池,这意味着:

即使并发任务很多,CPU 线程数量是有限的。

但这并不代表你可以毫无顾忌地创建几千甚至几万条任务。

来看下面这个示例:

for url in urls {   // urls 有 1000 条group.addTask {return await self.fetch(url)}
}

问题:

  • 这 1000 个 task 虽然不会全部同时运行
  • 它们都需要内存空间(约几十 KB 到几百 KB 不等)
  • 在某些场景下甚至会导致 OOM(特别是 iOS)

所以我们需要引入“最大并发数量”,避免一次性创建太多任务。

正确方式:限制并发数量,边消费边添加

下面这个优化版本是最推荐的写法:

import Foundation@MainActor
@Observable
final class Store {private(set) var messages: [String] = []func fetch(urls: [URL]) async {messages = await withTaskGroup(of: String.self,returning: [String].self) { group inlet maxConcurrent = min(urls.count, 10)var index = -1// 先添加有限数量的任务for _ in 0..<maxConcurrent {group.addTask {index += 1return await self.fetch(urls[index])}}var result: [String] = []// 每完成一个任务,再补一个新的任务进来for await message in group where !message.isEmpty {result.append(message)if index < urls.count - 1 {group.addTaskUnlessCancelled {index += 1return await self.fetch(urls[index])}}}return result}}private func fetch(_ url: URL) async -> String {try? await Task.sleep(for: .milliseconds(300))return "内容来自:\(url.absoluteString)"}
}

代码解析:为什么这样做更好?

1. 一次只保持 10 个任务在内存中

避免出现:

  • 1000 个 task 同时排队
  • 每个 task 占几十 KB
  • 最终占用几十 MB 内存

2. “边消费边补充”的思路就像:

像是请求工厂:最多 10 个工人同时工作,做完一个补一个。

3. addTaskUnlessCancelled

这个 API 会在 group 已经取消时拒绝添加新任务。
适用于:

  • 大文件下载
  • 批量数据处理
  • 任务优先级调整

实际项目中的典型使用场景

下面给你几个真实业务场景,TaskGroup 都非常适用:

批量网络请求

例如财务系统要同时拉取:

  • 订单列表
  • 用户详情
  • 账单列表
  • 审批记录

TaskGroup 可以更快得到全部数据。

批量文件读取

比如从沙盒加载大量 JSON 文件:

  • 配置文件
  • 日志文件
  • 离线数据包

使用 TaskGroup 可以并行读取,速度会快很多。

图像处理/视频处理

常见于:

  • 批量生成缩略图
  • OCR 识别
  • AI 图片分块处理(比如 webp/png 编码)

分布式任务(结合 Swift server)

例如你正在写一个 Vapor / Hummingbird 服务,通过 TaskGroup 做:

  • 并行访问多个微服务
  • 汇总返回结果
  • 自动取消无用请求

总结

TaskGroup 是 Swift 并发中非常值得掌握的能力,它帮助我们:

  • 更优雅地处理并行任务
  • 更安全地执行大量异步任务
  • 自动处理取消
  • 避免创建过多线程
  • 在大任务场景中极大节省内存

并且它已经 back-deploy 到 iOS 13 / macOS 10.15,所以你几乎可以在所有项目中放心使用。

http://www.dtcms.com/a/609219.html

相关文章:

  • csharp通过对象和模板字符串解析模板
  • MYSQL结构操作DDL指令1.数据库操作
  • 为什么会有免费制作网站wordpress建站腾讯云
  • 仓颉迁移实战:将 Node.js 微服务移植到 Cangjie 的工程化评测
  • Redis(六)——哨兵
  • 网站错敏词整改报告,如何整改后如何定期自查自检
  • 网站验收时项目建设总结报告网站建设与维护本科教材
  • 【Java】使用国密2,3,4.仿照https 统一请求响应加解密
  • 华为对象存储:nginx代理临时访问地址后访问报错:Authentication Failed
  • 【2025-11-13】软件供应链安全日报:最新漏洞预警与投毒预警情报汇总
  • 【玩转多核异构】T153核心板RISC-V核的实时性应用解析
  • 单周期Risc-V指令拆分与datapath绘制
  • Java+EasyExcel 打造学习平台视频学习时长统计系统
  • 【PHP】使用buildsql构造子查询
  • 防火墙主要有哪些类型?如何保护网络安全?
  • 在线商城网站制作如东住房和城乡建设局网站
  • Java 与 PHP 开发核心良好习惯笔记(含通用+语言特有)
  • AI 电影制作迈入新阶段:谷歌云Veo 3.1模型发布,实现音频全覆盖与精细化创意剪辑
  • C++函数式策略模式中配置修改
  • [MCP][]快速入门MCP开发
  • 为食堂写个网站建设免费毕业设计的网站建设
  • 云原生数据平台(cloudeon)--核心服务组件扩展
  • 字典或者列表常用方法介绍
  • 计算机网络中的地址体系全解析(包含 A/B/C 类地址 + 私有地址 + CIDR)
  • SpringBoot教程(三十四)| SpringBoot集成本地缓存Caffeine
  • 专业摄影网站推荐专业做卖菜的网站
  • Hadess V1.2.5版本发布,新增推送规则、制品扫描等,有效保障制品质量与安全
  • 华清远见25072班单片机高级学习day1
  • Apache Flink运行环境搭建
  • Node.js(v16.13.2版本)安装及环境配置教程