news 2026/6/19 10:45:50

线程同步的意义

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
线程同步的意义

一、C# 线程同步的核心概念与作用

线程同步是多线程编程中控制共享资源访问顺序的技术,目的是解决竞态条件(多个线程无序操作共享资源导致数据不一致),确保程序在多线程环境下的数据正确性行为可预测性

核心作用:
  1. 保证原子性:将多步操作(如a = a + 1)变为 “不可分割” 的原子操作,避免中间状态被其他线程干扰。
  2. 保证可见性:确保一个线程对共享变量的修改,能被其他线程立即感知(避免 CPU 缓存导致的 “脏读”)。
  3. 保证有序性:禁止编译器 / CPU 对指令的乱序优化,确保代码执行顺序符合预期。

二、C# 中常用的线程同步方式

1. 线程同步的意义(基础案例:未同步的问题)

问题场景:多个线程同时修改共享计数器,导致结果错误。

using System; using System.Threading; class ThreadSyncDemo { // 共享资源:未同步的计数器 private static int _counter = 0; static void Main() { Console.WriteLine("=== 未同步的计数器 ==="); // 启动10个线程同时递增计数器 for (int i = 0; i < 10; i++) { new Thread(IncrementCounter).Start(); } // 等待所有线程完成(简单模拟,实际应使用Join) Thread.Sleep(1000); Console.WriteLine($"最终计数器值:{_counter}"); // 预期10,实际可能小于10 } static void IncrementCounter() { // 非原子操作:读取→递增→写入 int temp = _counter; Thread.Sleep(1); // 模拟耗时操作,放大竞态条件 _counter = temp + 1; Console.WriteLine($"线程{Thread.CurrentThread.ManagedThreadId}:当前值={_counter}"); } }

输出(示例)

=== 未同步的计数器 === 线程4:当前值=1 线程5:当前值=1 线程6:当前值=2 线程7:当前值=3 ... 最终计数器值:8

原因:多个线程同时读取_counter的旧值,导致递增操作被覆盖。

2. 监视器(Monitor)与 lock 关键字

lockMonitor的语法糖,保证临界区代码互斥执行

using System; using System.Threading; class MonitorLockDemo { private static int _counter = 0; // 锁对象:必须是引用类型,且私有只读(避免外部干扰) private static readonly object _lockObj = new object(); static void Main() { Console.WriteLine("=== 使用lock的计数器 ==="); for (int i = 0; i < 10; i++) { new Thread(IncrementCounterWithLock).Start(); } Thread.Sleep(1000); Console.WriteLine($"最终计数器值:{_counter}"); // 稳定输出10 } static void IncrementCounterWithLock() { // lock自动包含try-finally,确保锁释放 lock (_lockObj) { int temp = _counter; Thread.Sleep(1); _counter = temp + 1; Console.WriteLine($"线程{Thread.CurrentThread.ManagedThreadId}:当前值={_counter}"); } } }

输出

=== 使用lock的计数器 === 线程4:当前值=1 线程5:当前值=2 线程6:当前值=3 ... 最终计数器值:10

原理lock会调用Monitor.Enter(_lockObj)获取锁,finally中调用Monitor.Exit(_lockObj)释放锁,确保同一时间只有一个线程进入临界区。

3. Lock(C# 13 新特性)

C# 13 引入的System.Threading.Lock,通过作用域自动管理锁,比Monitor更简洁高效。

using System; using System.Threading; using System.Threading.Tasks; class NewLockDemo { private static int _counter = 0; // C# 13的Lock类型 private static readonly Lock _newLock = new Lock(); static async Task Main() { Console.WriteLine("=== 使用C# 13 Lock的计数器 ==="); // 启动10个并发任务 Task[] tasks = new Task[10]; for (int i = 0; i < 10; i++) { tasks[i] = Task.Run(IncrementCounterWithNewLock); } await Task.WhenAll(tasks); Console.WriteLine($"最终计数器值:{_counter}"); // 稳定输出10 } static void IncrementCounterWithNewLock() { // using作用域结束时自动释放锁 using (_newLock.EnterScope()) { int temp = _counter; Thread.Sleep(1); _counter = temp + 1; Console.WriteLine($"线程{Thread.CurrentThread.ManagedThreadId}:当前值={_counter}"); } } }

优势:无需手动处理try-finally,锁生命周期与代码块一致,降低死锁风险。

4. volatile 关键字

volatile确保变量的可见性(禁止 CPU 缓存)和有序性(禁止指令重排),但不保证原子性。

using System; using System.Threading; class VolatileDemo { // volatile标记:确保线程间可见 private static volatile bool _isRunning = true; static void Main() { Console.WriteLine("=== volatile示例 ==="); new Thread(Worker).Start(); // 主线程修改_isRunning Thread.Sleep(1000); _isRunning = false; Console.WriteLine("主线程:已设置_isRunning=false"); } static void Worker() { int count = 0; // 若_isRunning不标记volatile,Worker可能永远无法感知到修改 while (_isRunning) { count++; } Console.WriteLine($"Worker线程:退出循环,执行次数={count}"); } }

输出

=== volatile示例 === 主线程:已设置_isRunning=false Worker线程:退出循环,执行次数=...

注意volatile仅适用于boolint等简单类型,复杂操作(如_counter++)仍需配合锁。

5. System.Threading.Interlocked

提供原子操作(如递增、交换),底层由 CPU 指令支持,比锁更高效。

using System; using System.Threading; class InterlockedDemo { private static int _counter = 0; static void Main() { Console.WriteLine("=== Interlocked原子操作 ==="); for (int i = 0; i < 10; i++) { new Thread(IncrementWithInterlocked).Start(); } Thread.Sleep(1000); Console.WriteLine($"最终计数器值:{_counter}"); // 稳定输出10 } static void IncrementWithInterlocked() { // 原子递增:读取→递增→写入是一个不可分割的操作 Interlocked.Increment(ref _counter); Console.WriteLine($"线程{Thread.CurrentThread.ManagedThreadId}:当前值={_counter}"); Thread.Sleep(1); } }

常用方法

  • Increment(ref int):原子递增
  • Decrement(ref int):原子递减
  • Exchange(ref T, T):原子交换值
  • CompareExchange(ref T, T, T):原子比较并交换
6. System.Threading.Mutex

Mutex(互斥锁)支持跨进程同步,比lock更重(涉及系统调用)。

using System; using System.Threading; class MutexDemo { private static int _counter = 0; // 命名Mutex:支持跨进程同步 private static readonly Mutex _mutex = new Mutex(false, "MyAppMutex"); static void Main() { Console.WriteLine("=== Mutex示例 ==="); for (int i = 0; i < 10; i++) { new Thread(IncrementWithMutex).Start(); } Thread.Sleep(1000); Console.WriteLine($"最终计数器值:{_counter}"); // 稳定输出10 } static void IncrementWithMutex() { // 请求获取Mutex(阻塞直到获取) _mutex.WaitOne(); try { int temp = _counter; Thread.Sleep(1); _counter = temp + 1; Console.WriteLine($"线程{Thread.CurrentThread.ManagedThreadId}:当前值={_counter}"); } finally { // 必须释放Mutex,否则其他线程永久阻塞 _mutex.ReleaseMutex(); } } }

场景:确保应用程序单实例运行、跨进程共享文件 / 硬件资源。

7. 重新发送事件(线程同步中的 “重试机制”)

当线程获取锁失败时,通过重试 + 延迟避免频繁竞争,提升性能。

using System; using System.Threading; class RetryEventDemo { private static int _counter = 0; private static readonly object _lockObj = new object(); static void Main() { Console.WriteLine("=== 重新发送事件(重试机制) ==="); for (int i = 0; i < 10; i++) { new Thread(IncrementWithRetry).Start(); } Thread.Sleep(2000); Console.WriteLine($"最终计数器值:{_counter}"); } static void IncrementWithRetry() { bool lockAcquired = false; // 重试最多5次,每次失败后延迟10ms for (int retry = 0; retry < 5 && !lockAcquired; retry++) { // 尝试获取锁(超时10ms) lockAcquired = Monitor.TryEnter(_lockObj, 10); if (lockAcquired) { try { int temp = _counter; Thread.Sleep(1); _counter = temp + 1; Console.WriteLine($"线程{Thread.CurrentThread.ManagedThreadId}:当前值={_counter}(第{retry+1}次尝试成功)"); } finally { Monitor.Exit(_lockObj); } } else { Console.WriteLine($"线程{Thread.CurrentThread.ManagedThreadId}:第{retry+1}次尝试获取锁失败,重试..."); } } } }

输出(示例)

=== 重新发送事件(重试机制) === 线程4:第1次尝试获取锁失败,重试... 线程5:第1次尝试获取锁成功,当前值=1 线程4:第2次尝试获取锁成功,当前值=2 ...
8. 线程本地存储(TLS)

为每个线程创建独立的变量副本,避免共享资源竞争(无需同步)。C# 中常用ThreadLocal<T>实现。

using System; using System.Threading; class ThreadLocalStorageDemo { // 每个线程有独立的计数器副本 private static ThreadLocal<int> _threadLocalCounter = new ThreadLocal<int>(() => 0); static void Main() { Console.WriteLine("=== 线程本地存储 ==="); // 启动3个线程,每个线程递增自己的副本 for (int i = 0; i < 3; i++) { new Thread(IncrementThreadLocalCounter).Start(); } Thread.Sleep(1000); Console.WriteLine("主线程:所有线程执行完毕"); } static void IncrementThreadLocalCounter() { for (int i = 0; i < 3; i++) { _threadLocalCounter.Value++; Console.WriteLine($"线程{Thread.CurrentThread.ManagedThreadId}:本地计数器={_threadLocalCounter.Value}"); Thread.Sleep(100); } // 释放资源(可选) _threadLocalCounter.Dispose(); } }

输出

=== 线程本地存储 === 线程4:本地计数器=1 线程5:本地计数器=1 线程6:本地计数器=1 线程4:本地计数器=2 线程5:本地计数器=2 ...

优势:完全避免线程间竞争,适用于 “每个线程独立状态” 的场景(如数据库连接缓存)。

三、总结

C# 线程同步的核心是控制共享资源的访问顺序,不同技术的适用场景:

技术
核心作用
适用场景
lock/Monitor互斥访问临界区大多数共享资源同步
System.Threading.Lock简化锁管理
C# 13 + 的高性能同步
volatile保证可见性 / 有序性简单变量的线程间状态同步
Interlocked原子操作简单数值的递增 / 交换
Mutex跨进程同步多进程共享资源
重新发送事件
减少锁竞争
高并发场景的锁重试
ThreadLocal<T>线程独立副本每个线程的私有状态

补充说明(选型核心原则)

  1. 优先选轻量级方案:能不用锁就不用(如 ThreadLocal)→ 能用原子操作就不用锁(Interlocked)→ 能用用户态锁(lock/Lock)就不用内核态锁(Mutex);
  2. 避免过度同步:仅对 “真正共享的资源” 加同步,线程私有数据直接用 TLS;
  3. 死锁规避
    • lock/Mutex 需保证 “锁的获取顺序一致”(如先锁 A 再锁 B,所有线程都遵循);
    • 尽量缩短临界区代码(只包裹必要操作,避免在锁内做 IO / 耗时计算);
  4. 性能权衡
    • 高并发计数器:优先 Interlocked,而非 lock;
    • 跨进程同步:只能用 Mutex;
    • 简单状态标记:用 volatile 替代 lock。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/19 21:48:50

数据结构算法篇洗牌算法(特别有意思的算法)

一、算法结构1.我们需要Card类来定义卡牌卡牌需要一个rank&#xff08;牌面数字&#xff09;&#xff0c;和一个suit&#xff08;花色&#xff09;注意要记得写一个toString方法public int rank;//牌面数字public String suit;//花色public Card(int rank, String suit) {this.…

作者头像 李华
网站建设 2026/6/19 9:37:56

论文生成源码排名:9大平台+开源开发工具

论文生成源码排名&#xff1a;9大平台开源开发工具 核心工具对比速览 工具名称 核心功能 处理时间 适配检测系统 特色优势 aibiye 论文降重与AIGC优化 15-30分钟 知网/维普/万方 语义级改写技术&#xff0c;保留学术逻辑 aicheck AIGC检测与降重 20分钟 知网/格子…

作者头像 李华
网站建设 2026/6/10 8:08:17

打造个人专属媒体王国:Jellyfin跨平台一键部署全攻略

还在为手机、电脑、电视上的媒体文件分散管理而头疼吗&#xff1f;想要随时随地欣赏自己的电影收藏却苦于找不到合适的解决方案&#xff1f;今天我要向你推荐一款完全免费、功能强大的个人媒体服务器软件——Jellyfin&#xff0c;让你轻松拥有属于自己的媒体王国&#xff01; 【…

作者头像 李华
网站建设 2026/6/19 9:18:57

文科通讯作者工具:8大平台+规范查询排名

文科通讯作者工具&#xff1a;8大平台规范查询排名 文科通讯作者工具&#xff1a;8大平台规范查询排名 核心工具对比速览 工具名称 核心功能 适用场景 效率评分 特色优势 aibiye 论文选题与框架生成 文科开题报告/文献综述 ★★★★☆ 文科专业适配度高 aicheck 开…

作者头像 李华
网站建设 2026/6/19 10:35:30

深蓝词库转换:跨平台输入法词库迁移终极指南

深蓝词库转换&#xff1a;跨平台输入法词库迁移终极指南 【免费下载链接】imewlconverter ”深蓝词库转换“ 一款开源免费的输入法词库转换程序 项目地址: https://gitcode.com/gh_mirrors/im/imewlconverter 你是否曾经因为更换输入法而苦恼于词库无法迁移&#xff1f;…

作者头像 李华
网站建设 2026/6/13 10:11:32

离线安装.net3.5 sp

把提取的SXS放在C盘根目录&#xff0c;在管理员模式下powershell或者cmd复制下面脚本直接运行。 把sxs文件夹放到C盘根目录了&#xff0c;那么直接用如下代码即可 dism.exe /online /enable-feature /featurename:netfx3 /Source:C:\sxs 源网址在&#xff1a; 源网址在 Win10…

作者头像 李华