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

聊透多线程编程-线程互斥与同步-13. C# Mutex类实现线程互斥

目录

一、什么是临界区?

二、Mutex类简介

三、Mutex的基本用法

解释:

四、Mutex的工作原理

五、使用示例1-保护共享资源

解释:

六、使用示例2-跨进程同步

示例场景

1. 进程A - 主进程

2. 进程B - 第二个进程

输出结果

ProcessA 的输出

ProcessB 的输出

解释

七、注意事项

八、总结


 

在多线程编程中,线程之间的同步和互斥是确保程序正确运行的重要机制。C# 提供了多种工具来实现线程同步,其中 Mutex 是一种功能强大的同步原语,特别适合用于跨进程的线程互斥场景。本文将详细介绍如何使用 Mutex 类实现线程互斥,并通过示例展示其工作原理。


一、什么是临界区?

在多线程编程中,临界区是指一段需要互斥访问的代码块,通常涉及对共享资源的操作。为了避免多个线程同时操作共享资源而导致数据竞争或状态不一致,我们需要对临界区代码进行保护。

例如,如果两个线程同时修改一个共享变量,可能会导致最终结果不符合预期。因此,我们需要一种机制来确保同一时间只有一个线程可以进入临界区。


二、Mutex类简介

Mutex(Mutual Exclusion)类是 .NET 提供的一种线程同步工具,用于实现线程间的互斥访问。与 lockMonitor 不同,Mutex 支持跨进程的线程同步,这使得它非常适合用于多进程环境下的资源保护。

Mutex 的主要特点包括:

  • 跨进程支持Mutex 可以在不同进程之间共享,适用于分布式或多进程应用。
  • 独占锁:同一时间只有一个线程(或进程)可以持有 Mutex
  • 命名支持:可以通过命名方式创建全局 Mutex,从而实现跨进程同步。

三、Mutex的基本用法

Mutex 的基本用法包括以下几个步骤:

  1. 创建 Mutex 对象。
  2. 调用 WaitOne 方法获取锁。
  3. 执行需要同步的代码块。
  4. 调用 ReleaseMutex 方法释放锁。

为了确保资源的正确释放,通常会将 Mutex 放入 using 块中,这样即使发生异常,也能保证资源被正确释放。

using System;
using System.Threading;class Program
{private static readonly Mutex _mutex = new Mutex(); // 创建 Mutex 对象static void Main(){Thread t1 = new Thread(DoWork);Thread t2 = new Thread(DoWork);t1.Start();t2.Start();t1.Join();t2.Join();}static void DoWork(){Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId}: Waiting for mutex...");_mutex.WaitOne(); // 获取锁try{Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId}: Entered critical section.");Thread.Sleep(2000); // 模拟一些工作}finally{_mutex.ReleaseMutex(); // 释放锁Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId}: Released mutex.");}}
}

解释:

  • _mutex.WaitOne():尝试获取锁。如果锁已被占用,则当前线程会被阻塞,直到锁可用。
  • _mutex.ReleaseMutex():释放锁,允许其他线程获取该锁。
  • 使用 try-finally 块是为了确保即使发生异常,锁也能被正确释放。

四、Mutex的工作原理

Mutex 的核心思想是基于操作系统级别的信号量机制,提供了更高级别的同步能力:

  1. 当线程调用 WaitOne 方法时,它会尝试获取 Mutex。如果 Mutex 已被其他线程占用,则当前线程会被挂起,直到 Mutex 可用。
  2. 当线程调用 ReleaseMutex 方法时,它会释放 Mutex,允许其他线程获取该锁。
  3. 如果 Mutex 是命名的(通过构造函数指定名称),它可以跨进程共享,从而实现跨进程同步。

五、使用示例1-保护共享资源

下面是一个使用 Mutex 类保护共享资源的例子:

using System;
using System.Threading;class Program
{private static int _counter = 0;private static readonly Mutex _mutex = new Mutex();static void Main(){Thread t1 = new Thread(IncrementCounter);Thread t2 = new Thread(IncrementCounter);t1.Start();t2.Start();t1.Join();t2.Join();_mutex.Dispose();Console.WriteLine($"Final Counter Value: {_counter}");}static void IncrementCounter(){for (int i = 0; i < 100000; i++){_mutex.WaitOne();try{_counter++;}finally{_mutex.ReleaseMutex();}}}
}

解释:

  • _mutex 是一个静态对象,用于标识锁。
  • 每次访问 _counter 时,都会通过 WaitOne 获取锁,并通过 ReleaseMutex 释放锁。
  • 最终输出的结果是 200000,因为所有线程的操作都被正确同步了。

六、使用示例2-跨进程同步

Mutex 的跨进程同步能力使其非常适合用于分布式或多进程环境中的资源共享和互斥访问。下面通过一个完整的例子,演示如何使用命名的 Mutex 来实现跨进程同步。

示例场景

假设我们有两个独立的应用程序(进程),它们都需要访问一个共享资源(例如文件或数据库)。为了避免数据竞争,我们需要确保同一时间只有一个进程可以访问该资源。我们将使用命名的 Mutex 来实现这一目标。

1. 进程A - 主进程

这是第一个应用程序,它尝试获取 Mutex 并独占访问共享资源。

// ProcessA.cs
using System;
using System.Threading;class Program
{static void Main(string[] args){// 创建一个命名的 Mutexbool isCreatedNew; // 是否是第一个创建 Mutex 的进程using (Mutex mutex = new Mutex(true, "Global\\SharedResourceMutex", out isCreatedNew)){if (isCreatedNew){Console.WriteLine("Process A: This process owns the mutex.");Console.WriteLine("Process A: Accessing shared resource...");// 模拟对共享资源的操作Thread.Sleep(50000); // 假设操作需要 5 秒                Console.WriteLine("Process A: Releasing mutex.");//释放锁mutex.ReleaseMutex();}else{Console.WriteLine("Process A: Another process already owns the mutex. Waiting...");// 等待其他进程释放 Mutexmutex.WaitOne();Console.WriteLine("Process A: Acquired mutex after waiting.");// 模拟对共享资源的操作Console.WriteLine("Process A: Accessing shared resource...");Thread.Sleep(5000);Console.WriteLine("Process A: Releasing mutex.");//释放锁mutex.ReleaseMutex();}}}
}

2. 进程B - 第二个进程

这是第二个应用程序,它也会尝试获取同一个 Mutex,并访问共享资源。

// ProcessB.cs
using System;
using System.Threading;class Program
{static void Main(string[] args){// 创建一个命名的 Mutexbool isCreatedNew; // 是否是第一个创建 Mutex 的进程using (Mutex mutex = new Mutex(true, "Global\\SharedResourceMutex", out isCreatedNew)){try{if (isCreatedNew){Console.WriteLine("Process B: This process owns the mutex.");Console.WriteLine("Process B: Accessing shared resource...");// 模拟对共享资源的操作Thread.Sleep(5000); // 假设操作需要 5 秒Console.WriteLine("Process B: Releasing mutex.");//释放锁mutex.ReleaseMutex();}else{Console.WriteLine("Process B: Another process already owns the mutex. Waiting...");// 等待其他进程释放 Mutexmutex.WaitOne();Console.WriteLine("Process B: Acquired mutex after waiting.");// 模拟对共享资源的操作Console.WriteLine("Process B: Accessing shared resource...");Thread.Sleep(5000);Console.WriteLine("Process B: Releasing mutex.");//释放锁mutex.ReleaseMutex();}}catch (AbandonedMutexException){Console.WriteLine("Process B: Detected an abandoned mutex. Continuing execution...");// 即使检测到被遗弃的 Mutex,当前线程仍然可以继续执行。}}}
}

输出结果

ProcessA 的输出

Process A: This process owns the mutex.
Process A: Accessing shared resource...
Process A: Releasing mutex.

ProcessB 的输出

Process B: Another process already owns the mutex. Waiting...
Process B: Acquired mutex after waiting.
Process B: Accessing shared resource...
Process B: Releasing mutex.

解释

  1. 命名的 Mutex

    • 在 new Mutex(true, "Global\\SharedResourceMutex", out isCreatedNew) 中,"Global\\SharedResourceMutex" 是 Mutex 的名称。
    • Global\\ 前缀表示该 Mutex 是全局的,可以在不同进程之间共享。
  2. 跨进程同步

    • 当 ProcessA 创建 Mutex 时,isCreatedNew 为 true,表示它是第一个创建该 Mutex 的进程。
    • 当 ProcessB 尝试创建同名的 Mutex 时,isCreatedNew 为 false,表示该 Mutex 已存在,并由另一个进程持有。
  3. 等待与释放

    • 如果 Mutex 已被占用,调用 mutex.WaitOne() 会使当前线程阻塞,直到 Mutex 被释放。
    • 调用 mutex.ReleaseMutex() 会释放 Mutex,允许其他线程或进程获取它。

七、注意事项

  1. 命名冲突

    • 命名的 Mutex 必须具有唯一性,避免与其他应用程序发生冲突。
    • 可以使用 GUID 或特定的前缀来确保名称的唯一性。
  2. 性能开销

    • Mutex 是基于操作系统级别的同步机制,性能开销较大,尤其是在高并发场景下。
    • 对于单进程内的线程同步,推荐使用 lock 或 Monitor
  3. 死锁风险

    • 如果一个进程获取了 Mutex 但未释放,会导致其他进程永远无法获取锁。
    • 确保在 finally 块中调用 ReleaseMutex,避免因异常导致锁未释放。
  4. 权限问题

    • 在某些情况下,创建全局 Mutex 可能需要管理员权限,尤其是在 Windows 系统中。
  5. 为什么使用 using

    • 将 Mutex 放入 using 块中可以确保资源的正确释放,避免资源泄漏。
    • 即使发生异常,Dispose 方法也会被自动调用,保证资源管理的安全性和可靠性。

八、总结

Mutex 类是 C# 中实现线程互斥的一种重要工具,特别适合用于跨进程的线程同步场景。尽管它的使用稍微复杂一些,但能够满足更多高级需求,例如分布式系统中的资源保护。

在实际开发中,选择合适的同步机制非常重要。对于简单的线程互斥场景,lockMonitor 可能更为直观;而对于需要跨进程同步的场景,Mutex 则是一个不错的选择。通过合理利用 Mutex,我们可以有效避免数据竞争和资源冲突,确保多线程或多进程应用的稳定性和可靠性。

 

相关文章:

  • 图片压缩工具,多种压缩方案可选
  • requestAnimationFrame是什么?【前端】
  • 基于瑞芯微RK3576国产ARM八核2.2GHz A72 工业评估板——ROS2系统使用说明
  • MH2103系列coremark1.0跑分数据和优化,及基于arm2d的优化应用
  • 鸿蒙NEXT开发LRUCache缓存工具类(单例模式)(ArkTs)
  • Gmssl实战
  • OpenSSL1.1.1d windows安装包资源使用
  • 【C++编程入门】:从零开始掌握基础语法
  • Python常用的第三方模块【openpyxl库】读写Excel文件
  • Vue路由传参的几种方式-案例
  • 系统分析师知识点:访问控制模型OBAC、RBAC、TBAC与ABAC的对比与应用
  • ONLYOFFICE协作空间3.1发布:虚拟数据房间中基于角色的表单填写、房间模板、改进访客管理等
  • 利用WSL2的镜像功能访问Windows下的所有网卡
  • 日志文件太大,如何分卷压缩便于传输
  • 第 2 篇:初探时间序列 - 可视化与基本概念
  • 【网络编程】从零开始彻底了解网络编程(三)
  • IQ信号和实信号的关系与转换的matlab实现
  • 软件工程师中级考试-上午知识点总结(上)
  • Docker概念详解
  • Netdata 监控多台服务器
  • 全国总工会成立100周年,工运历史和发展成就展将对外展出
  • 庆祝中国印尼建交75周年招待会暨万隆会议70周年纪念活动在京举行
  • 空山日落雨初收,来文徵明的画中听泉
  • 韩国新一届总统选举将于6月3日举行,民调显示李在明继续领跑
  • 4月LPR保持不变:1年期3.1%,5年期以上3.6%
  • 十大券商看后市|A股下行波动风险有限,震荡中有望逐步抬升