news 2026/5/25 19:21:20

线程同步的意义

作者头像

张小明

前端开发工程师

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/5/23 17:55:31

Java被裁失业如何快速上岸?

谈到Java面试&#xff0c;相信大家第一时间脑子里想到的词肯定是金三银四&#xff0c;金九银十。好像大家的潜意识里做Java开发的都得在这个时候才能出去面试&#xff0c;跳槽成功率才高&#xff01;但LZ不这么认为&#xff0c;LZ觉得我们做技术的一生中会遇到很多大大小小的面…

作者头像 李华
网站建设 2026/5/25 19:20:48

如何快速掌握洛雪音乐音源配置:搭建个人音乐库的完整教程

还在为音乐版权分散、平台切换频繁而烦恼&#xff1f;洛雪音乐音源项目为你提供了一站式解决方案&#xff0c;让你轻松搭建专属免费音乐库。本文将带你从零开始&#xff0c;快速掌握音源配置的核心技巧。 【免费下载链接】lxmusic- lxmusic(洛雪音乐)全网最新最全音源 项目地…

作者头像 李华
网站建设 2026/5/25 13:14:12

Docker容器化部署魔兽世界服务器:新手友好的一键搭建指南

Docker容器化部署魔兽世界服务器&#xff1a;新手友好的一键搭建指南 【免费下载链接】azerothcore-wotlk Complete Open Source and Modular solution for MMO 项目地址: https://gitcode.com/GitHub_Trending/az/azerothcore-wotlk 还在为搭建AzerothCore-WoTLK服务器…

作者头像 李华
网站建设 2026/5/21 21:44:13

Orleans分布式追踪实战:从工具选型到部署优化

Orleans分布式追踪实战&#xff1a;从工具选型到部署优化 【免费下载链接】orleans dotnet/orleans: Orleans是由微软研究团队创建的面向云应用和服务的分布式计算框架&#xff0c;特别适合构建虚拟 actor模型的服务端应用。Orleans通过管理actors生命周期和透明地处理网络通信…

作者头像 李华
网站建设 2026/5/20 15:08:32

3步轻松上手Phi-3:AI小模型大能量的完整使用指南

3步轻松上手Phi-3&#xff1a;AI小模型大能量的完整使用指南 【免费下载链接】Phi-3-mini-4k-instruct-gguf 项目地址: https://ai.gitcode.com/hf_mirrors/ai-gitcode/Phi-3-mini-4k-instruct-gguf 想要快速体验AI模型的强大功能却担心配置复杂&#xff1f;Phi-3-mini…

作者头像 李华
网站建设 2026/5/23 21:40:43

论文提纲生成工具排名:AI如何帮你一键搞定论文框架

论文提纲生成工具排名&#xff1a;AI如何帮你一键搞定论文框架 论文提纲生成工具核心对比 工具名称 生成速度 逻辑性 学术深度 适用阶段 AIbiye ⚡⚡⚡⚡ ⭐⭐⭐⭐ ⭐⭐⭐ 选题/大纲 AICheck ⚡⚡⚡ ⭐⭐⭐⭐ ⭐⭐⭐⭐ 大纲/开题 AskPaper ⚡⚡ ⭐⭐⭐ ⭐⭐…

作者头像 李华