当前位置: 首页 > 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,我们可以有效避免数据竞争和资源冲突,确保多线程或多进程应用的稳定性和可靠性。


http://www.mrgr.cn/news/100644.html

相关文章:

  • 目标检测篇---faster R-CNN
  • HarmonyOS NEXT 诗词元服务项目开发上架全流程实战(一、项目介绍及实现效果)
  • CogCoM: A Visual Language Model with Chain-of-Manipulations Reasoning 学习笔记
  • 【金仓数据库征文】加速数字化转型:金仓数据库在金融与能源领域强势崛起
  • 51c大模型~合集122
  • 2025年五一数学建模竞赛AI辅助全网专业性第一
  • 在线图书管理系统的结构化需求分析过程讲解
  • Spark知识总结
  • 隐形革命:环境智能如何重构“人-机-境“共生新秩序
  • lmms-eval--微调实战笔记
  • 52.[前端开发-JS实战框架应用]Day03-AJAX-插件开发-备课项目实战-Lodash
  • 相机-IMU联合标定:相机标定
  • 如何让自己的博客可以在百度、谷歌、360上搜索到(让自己写的CSDN博客可以有更多的人看到)
  • vue3代码规范管理;基于vite和vue3、 eslint、prettier、stylelint、husky规范;git触发eslint校验
  • 【Castle-X机器人】模块安装与调试
  • FFTW3.3.10库与QT结合的使用
  • Ant(Ubuntu 18.04.6 LTS)安装笔记
  • IP查询专业版:支持IPv4/IPv6自动识别并切换解析的API接口使用指南
  • Ubuntu安装SRS流媒体服务
  • 打印及判断回文数组、打印N阶数组、蛇形矩阵