Swift 并发编程新选择:Mutex 保护可变状态实战解析

作者:unravel2025日期:2025/10/29

前言

Swift 5.5 带来 async/await 与 Actor 后,「用 Actor 包一层」几乎成了默认答案。

但在日常开发里,我们经常会遇到两种尴尬:

  1. 只想保护一个计数器、缓存或 token,却不得不把整段逻辑都改成异步;
  2. 把对象放到 @MainActor 后,发现后台线程也要用,结果到处是 await。

Apple 在 Swift 5.9 前后把 Mutex 正式搬进标准库(通过 Synchronization 模块),给“同步但不想异步”的场景提供了第三条路。

Mutex 是什么(一句话先记住)

Mutex = 互斥锁,同步、阻塞、轻量。

它只干一件事:同一时刻最多一个线程进入临界区,保证对共享状态的“读-改-写”原子化。

与 Actor 的“异步消息”不同,Mutex 的等待是阻塞线程,所以临界区必须短、快、不阻塞。

基础用法:从 0 到 1 保护一个计数器

  1. 引入模块(Xcode 15+/Swift 5.9 自带)
1import Synchronization
2
  1. 定义线程安全的 Counter
1final class Counter: Sendable {          // ① Sendable 空标记即可,Mutex 本身已 Sendable
2    private let mutex = Mutex(0)         // ② 初始值 0
3
4    /// 加 1,同步返回
5    func increment() {
6        mutex.withLock { value in
7            value += 1                   // ③ 闭包内 value 是 inout,直接改
8        }
9    }
10
11    /// 减 1
12    func decrement() {
13        mutex.withLock { value in
14            value -= 1
15        }
16    }
17
18    /// 读值,也要拿锁
19    var count: Int {
20        mutex.withLock { value in
21            return value                 // ④ 只读,同样原子
22        }
23    }
24}
25
  1. 客户端代码——完全同步
1let counter = Counter()
2counter.increment()
3print(counter.count)   // 1
4

要点回顾

  • withLock<T> 泛型返回,既能读也能写;
  • 闭包里的 valueinout,修改即生效;
  • 锁的持有时间 = 闭包运行时间,务必短。

让属性看起来“像正常变量”——封装 getter/setter

1extension Counter {
2    var count: Int {
3        get {
4            mutex.withLock { $0 }        // $0 就是 value,直接返回
5        }
6        set {
7            mutex.withLock { value in
8                value = newValue
9            }
10        }
11    }
12}
13
14// 使用方无感
15counter.count = 10
16print(counter.count) // 10
17

与 @Observable 搭档——让 SwiftUI 刷新

Mutex 只保护值,不会触发属性观察器。

若直接 @Observable final class Counter,视图不会刷新。

需要手动告诉 Observation 框架:

1@Observable
2final class Counter: Sendable {
3    private let mutex = Mutex(0)
4
5    var count: Int {
6        get {
7            access(keyPath: \.count)          // ① 读标记
8            return mutex.withLock { $0 }
9        }
10        set {
11            withMutation(keyPath: \.count) {  // ② 写标记
12                mutex.withLock { $0 = newValue }
13            }
14        }
15    }
16}
17

SwiftUI 端无额外成本:

1struct ContentView: View {
2    @State private var counter = Counter()
3
4    var body: some View {
5        VStack {
6            Text("\(counter.count)")
7            Button("++") { counter.increment() }
8            Button("--") { counter.decrement() }
9        }
10    }
11}
12

Actor or Mutex?一张决策表帮你 10 秒选

维度MutexActor
同步/异步同步、阻塞异步、非阻塞
适用场景极短临界区(赋值、累加)长时间任务、IO、网络
性能极轻量,纳秒级锁微秒毫秒,调度开销
语法侵入无 async强制 async/await
SendableMutex 已 Sendable,类标即可Actor 引用即 Sendable
调试难度简单,栈清晰异步堆栈难追踪

“只想保护一两行, Mutex 别犹豫; 流程长、要并发, Actor 顶上。”

扩展场景实战

  1. 高频读写缓存(图片、Token)
1final class ImageCache: Sendable {
2    private let cache = Mutex([String: Image]())
3
4    func image(for key: String) -> Image? {
5        cache.withLock { $0[key] }
6    }
7
8    func save(_ image: Image, for key: String) {
9        cache.withLock { dict in
10            dict[key] = image
11        }
12    }
13}
14
  1. 统计接口 QPS
1final class Stats: Sendable {
2    private let counter = Mutex(0)
3    private let start = Date()
4
5    func record() {
6        counter.withLock { $0 += 1 }
7    }
8
9    var qps: Double {
10        counter.withLock { Double($0) / start.timeIntervalSinceNow * -1 }
11    }
12}
13
  1. 保护非 Sendable 的 C 句柄
1final class SQLiteHandle: @unchecked Sendable {
2    private let db: UnsafeMutableRawPointer
3    public init(db: UnsafeMutableRawPointer) {
4        self.db = db
5    }
6    
7    private let lock = Mutex(())
8
9    func execute(_ sql: String) {
10        lock.withLock { _ in
11            sqlite3_exec(db, sql, nil, nil, nil)   // 临界区
12        }
13    }
14}
15

踩坑与提醒

  1. 长任务别用 Mutex
    一旦临界区阻塞 IO,整个线程池都会被卡死,比 Actor 还惨。
  2. 递归加锁会死锁
    Mutex 不可重入,同一线程重复拿锁直接挂起;Actor 不会。
  3. 锁粒度要细
    大对象整颗锁会变成性能瓶颈,可拆成多颗 Mutex 或按 Key 分片。
  4. Swift 6 数据竞争检查
    打开 -strict-concurrency=complete 后,凡是非 Sendable 全局变量都会报错;用 Mutex 包一层即可通过。

小结

Actor 把“线程安全”装进黑盒子,让开发者用消息思考;Mutex 把“锁”暴露给你,却换回最简洁的同步代码。

两者不是谁取代谁,而是互补:

  • 短、频、快 → Mutex
  • 长、流、异步 → Actor

Swift 并发编程新选择:Mutex 保护可变状态实战解析》 是转载文章,点击查看原文


相关推荐


F032 材料科学文献知识图谱可视化分析系统(四种知识图谱可视化布局) | vue + flask + echarts + d3.js 实现
B站麦麦大数据2025/10/26

文章结尾部分有CSDN官方提供的学长 联系方式名片 关注B站,有好处! 编号: F032 视频 neo4j 文献知识图谱可视化分析系统 | vue + flask + echarts + d3.js 实现 1 系统简介 系统简介:本系统是一个基于Vue+Flask构建的材料科学文献知识图谱可视化分析系统,其核心功能围绕文献数据的抓取、分析、可视化和用户管理展开。主要包括:主页模块,用于展示最新文献卡片,方便用户快速了解最新动态;文献搜索功能,支持用户通过关键词或其他条


【DeepSeek新开源】DeepSeek-OCR如何用“视觉压缩”革新长文本处理
kakaZhui2025/10/23

最近DeepSeek团队刚放出DeepSeek-OCR项目,不再将其视为一个简单的OCR(光学字符识别)工具,而是将其作为一个开创性的实验平台,旨在探索和验证一个激进的理念:我们能否利用视觉模态作为一种超高效的文本信息压缩媒介? 即,将长篇的数字文本“渲染”成一张图像,再用一个强大的视觉语言模型(VLM)从这张图像中“读”出原文。 接下来我们一起看下DeepSeek-OCR从“视觉压缩”的核心哲学,到其创新的DeepEncoder架构和多分辨率支持,再到其庞大的数据工程和训练管线。 1. 引


想偷卷?但微信不支持md文档?这个软件助你!
前端AC2025/10/22

📝 Markdown 查看器 - 现代化的文档预览工具 一个基于 React 19 + TypeScript 构建的现代化 Markdown 文档查看器,支持实时预览、语法高亮、数学公式渲染等功能。 在微信或浏览器上打开此编辑器,上传你的md文档可以上课偷偷看自己写的博客哈哈,这个是我解决微信这个没有md预览的痛点,自己用ai搞了一个小工具出来,效果还不错,还有图片可以借助图床工具:图床 - 简单、快速、免费的图床把自己图片上传到这里,就不会导致路径问题了。 项目概述 项目背景 在日常开发


告别页面呆板!这5个DOM操作技巧让你的网站活起来
良山有风来2025/10/21

你是不是经常遇到这样的情况:精心设计的页面看起来很美,但用户操作起来却毫无反应?点击按钮没反馈,表单提交没提示,页面切换生硬得像在翻纸质书? 这就像给用户端上了一盘色香味俱全的菜,结果吃起来却发现是冷的。问题就出在——你还没有掌握DOM操作的真正精髓。 今天,我就带你彻底搞懂JavaScript DOM操作,从基础到实战,让你的网页真正“活”起来。读完这篇文章,你不仅能理解DOM的工作原理,还能掌握5个让用户体验飙升的实用技巧。 什么是DOM?它为什么如此重要? 简单来说,DOM就是连接HTML


JVM 调优黄金三步法:监控→分析→验证
老K的Java兵器库2025/10/19

JVM 调优黄金三步法:监控→分析→验证 (方法论 + 案例 + 压测验证,新手也能照抄) 关键词:JVM 调优、监控、分析、验证、压测、方法论、黄金三步 阅读时长:20 min 环境:CentOS 7 + OpenJDK 8u342 + SpringBoot 1.5 + JMeter 5 适合:1~5 年 Java 开发、生产调优无思路、面试「JVM 怎么调优」标准答案 一、0 基础速记:黄金三步一句话 步骤目标一句话监控发现瓶颈先知道「哪里慢」再动手分析定位根因用数据证


Windows下Jenkins服务未自动重启问题解决
一张假钞2025/10/18

个人博客地址:Windows下Jenkins服务未自动重启问题解决 | 一张假钞的真实世界 成功安装 Jenkins 服务后,有时开机后 Jenkins 服务未自动启动。查看 Jenkins 服务安装目录下的日志发现没有服务启动的日志,所以猜测是系统启动后 Jenkins 服务未调起。 通过按 Win + R,然后输入 services.msc 并按回车来打开服务管理工具。找到 Jenkins 服务,点击右键,查看“属性”,Jenkins 默认设置如下: 为了每次开机能自动启动 Jen


【ComfyUI】电商模特面部融合
Mr数据杨2025/10/16

今天给大家展示一个 适用于相同脸型商品图生成的ComfyUI工作流,该工作流可高效处理两张来源图像,经过面部对齐、区域裁剪、图像融合与生成过程,快速构建视觉一致性强、适用于电商场景的最终图像输出。整体流程融合了 FluxKontext 模型推理、面部区域对齐处理、条件控制生成以及结果拼接输出等关键模块,极大提升图像一致性与真实感,适用于商品营销图、模特换穿搭图、广告图生成等多种需求场景。 文章目录 工作流介绍核心模型Node节点 工作流程应用场景开发与应用 工作流介绍 本工


ELK运维之路(Logstash7&Kibana接入ES集群-7.17.24)
会飞的小蛮猪2025/10/15

书接前文,本章介绍Logstash和Kibana组件的部署,测试环境哦别干生产,如有帮助到您请给个免费的赞呗! 1.Logstash 1.1 Docker-compose 配置片段 root@ubuntu2204test99:~/elkf# vi docker-compose.yml logstash: image: logstash:7.17.24 container_name: logstash-7.17.24 restart: always en


智能合约在分布式密钥管理系统中的应用
安当加密2025/10/14

非常好的问题!下面我将用通俗易懂 + 技术准确的方式,为你详细解释: 一、什么是智能合约(Smart Contract)? 简单比喻: 智能合约 = 自动售货机 你投入硬币(输入条件);机器自动判断金额是否足够(逻辑判断);如果满足,自动掉出饮料(执行结果);全程无需店员介入,规则透明、自动执行。 技术定义: 智能合约是运行在区块链上的、可编程的、自动执行的协议代码。它: 以代码形式定义规则(如“只有A和B同时签名,才能使用密钥”);部署在区块链上,不可篡改;当预设条件满足时,自动执行(


触摸未来2025.10.12:图景之锚,在多模态记忆中寻找记忆的本质
可触的未来,发芽的智生2025/10/12

《图景之锚:在多模态记忆中寻找记忆的本质》   心理学与神经认知科学的研究如一道强光,照进了我混沌的实验思路。个体的记忆并非以语言形式储存,而是以图景、场面、动作、感官体验等多模态图式构成的——这个发现让我重新审视了整个记忆系统的理论基础。   ---   我开始理解,在我们为事物命名之前,个体拥有的是一种极其丰富而未被语言化的记忆场域。那个场域里充斥着光影的流动、温度的变迁、肌体的触感、情绪的波动。这些原始的记忆素材如同未加工的宝石,散落在意识的各个角落。   命名所做的,

首页编辑器站点地图

Copyright © 2025 聚合阅读

License: CC BY-SA 4.0