08、使用互斥量在进程间进行同步
使用互斥量在进程间进行同步
知识点
什么是Mutex
Mutex(互斥量)是一种同步原语,不仅可以在同一进程的线程间进行同步,还可以在不同进程间进行同步。Mutex确保同一时间只有一个线程或进程可以访问共享资源。
Mutex的特点
- 跨进程同步:可以在不同进程间共享
- 命名支持:支持命名Mutex,便于进程间识别
- 所有权概念:只有获得Mutex的线程才能释放它
- 递归支持:同一线程可以多次获得同一个Mutex
- 系统级资源:由操作系统管理
Mutex vs Lock
| 特性 | Mutex | Lock |
|---|---|---|
| 跨进程 | 支持 | 不支持 |
| 性能 | 较慢 | 较快 |
| 功能 | 丰富 | 简单 |
| 超时 | 支持 | 不直接支持 |
使用场景
- 防止应用程序多次启动
- 进程间资源同步
- 跨进程的临界区保护
- 分布式系统中的资源协调
代码案例
案例1:防止应用程序多次启动
using System;
using System.Threading;
class SingleInstanceApp
{
private static Mutex appMutex;
private const string MutexName = "Global\\MyUniqueApplicationMutex";
static void Main(string[] args)
{
Console.WriteLine("单实例应用程序示例");
bool isNewInstance;
// 创建命名互斥量
appMutex = new Mutex(true, MutexName, out isNewInstance);
if (isNewInstance)
{
Console.WriteLine("这是应用程序的第一个实例");
RunApplication();
}
else
{
Console.WriteLine("应用程序已经在运行!");
Console.WriteLine("按任意键退出...");
Console.ReadKey();
}
// 释放互斥量
if (isNewInstance)
{
appMutex.ReleaseMutex();
}
appMutex.Dispose();
}
static void RunApplication()
{
Console.WriteLine("应用程序正在运行...");
Console.WriteLine("按任意键退出...");
// 模拟应用程序运行
for (int i = 1; i <= 10; i++)
{
Console.WriteLine($"应用程序工作中... {i}/10");
Thread.Sleep(1000);
// 检查是否有键盘输入
if (Console.KeyAvailable)
{
Console.ReadKey();
break;
}
}
Console.WriteLine("应用程序即将退出");
}
}
案例2:进程间共享资源访问
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
class InterProcessResourceAccess
{
private static readonly string MutexName = "Global\\SharedResourceMutex";
private static readonly string SharedFilePath = Path.Combine(Path.GetTempPath(), "shared_resource.txt");
static void Main(string[] args)
{
Console.WriteLine("进程间资源访问示例");
Console.WriteLine($"共享文件路径: {SharedFilePath}");
Console.WriteLine("启动多个此程序实例来观察同步效果");
// 模拟多个线程竞争资源
Task[] tasks = new Task[3];
for (int i = 0; i < tasks.Length; i++)
{
int threadId = i;
tasks[i] = Task.Run(() => AccessSharedResource(threadId));
}
Task.WaitAll(tasks);
Console.WriteLine("所有任务完成,按任意键退出...");
Console.ReadKey();
}
static void AccessSharedResource(int threadId)
{
using (var mutex = new Mutex(false, MutexName))
{
for (int i = 0; i < 5; i++)
{
try
{
Console.WriteLine($"线程{threadId}: 尝试获取互斥量... (第{i+1}次)");
// 尝试获取互斥量,设置超时时间
if (mutex.WaitOne(TimeSpan.FromSeconds(5)))
{
Console.WriteLine($"线程{threadId}: 获得互斥量,开始访问共享资源");
// 访问共享资源(文件)
WriteToSharedFile(threadId, i);
// 模拟工作时间
Thread.Sleep(2000);
Console.WriteLine($"线程{threadId}: 完成资源访问");
}
else
{
Console.WriteLine($"线程{threadId}: 获取互斥量超时");
}
}
catch (AbandonedMutexException ex)
{
Console.WriteLine($"线程{threadId}: 检测到被放弃的互斥量: {ex.Message}");
}
finally
{
try
{
mutex.ReleaseMutex();
Console.WriteLine($"线程{threadId}: 释放互斥量");
}
catch (Exception ex)
{
Console.WriteLine($"线程{threadId}: 释放互斥量失败: {ex.Message}");
}
}
Thread.Sleep(1000); // 等待一段时间再次尝试
}
}
}
static void WriteToSharedFile(int threadId, int operation)
{
try
{
string content = $"线程{threadId} - 操作{operation} - 时间: {DateTime.Now:HH:mm:ss.fff}" + Environment.NewLine;
File.AppendAllText(SharedFilePath, content);
Console.WriteLine($"线程{threadId}: 成功写入文件");
// 读取并显示文件内容
if (File.Exists(SharedFilePath))
{
string[] lines = File.ReadAllLines(SharedFilePath);
Console.WriteLine($"线程{threadId}: 文件当前有 {lines.Length} 行");
}
}
catch (Exception ex)
{
Console.WriteLine($"线程{threadId}: 文件操作失败: {ex.Message}");
}
}
}
案例3:高级Mutex使用模式
using System;
using System.Security.AccessControl;
using System.Security.Principal;
using System.Threading;
using System.Threading.Tasks;
class AdvancedMutexUsage
{
private static readonly string MutexName = "Global\\AdvancedMutexExample";
static void Main(string[] args)
{
Console.WriteLine("高级Mutex使用示例");
// 演示不同的Mutex创建方式
DemonstrateBasicMutex();
Thread.Sleep(1000);
DemonstrateSecureMutex();
Thread.Sleep(1000);
DemonstrateTimeoutHandling();
Console.WriteLine("演示完成,按任意键退出...");
Console.ReadKey();
}
static void DemonstrateBasicMutex()
{
Console.WriteLine("\n=== 基本Mutex使用 ===");
using (var mutex = new Mutex(false, "Local\\BasicMutexExample"))
{
Task[] tasks = new Task[3];
for (int i = 0; i < tasks.Length; i++)
{
int taskId = i;
tasks[i] = Task.Run(() =>
{
try
{
Console.WriteLine($"任务{taskId}: 等待互斥量...");
mutex.WaitOne();
Console.WriteLine($"任务{taskId}: 获得互斥量,执行工作");
Thread.Sleep(1000);
Console.WriteLine($"任务{taskId}: 工作完成");
}
finally
{
mutex.ReleaseMutex();
Console.WriteLine($"任务{taskId}: 释放互斥量");
}
});
}
Task.WaitAll(tasks);
}
}
static void DemonstrateSecureMutex()
{
Console.WriteLine("\n=== 安全Mutex使用 ===");
try
{
// 创建具有安全描述符的Mutex
var mutexSecurity = new MutexSecurity();
mutexSecurity.AddAccessRule(new MutexAccessRule(
new SecurityIdentifier(WellKnownSidType.WorldSid, null),
MutexRights.FullControl,
AccessControlType.Allow));
bool createdNew;
using (var mutex = new Mutex(false, "Global\\SecureMutexExample", out createdNew, mutexSecurity))
{
Console.WriteLine($"安全Mutex创建: {(createdNew ? "新建" : "已存在")}");
if (mutex.WaitOne(3000))
{
Console.WriteLine("获得安全Mutex,执行受保护的操作");
Thread.Sleep(1000);
mutex.ReleaseMutex();
Console.WriteLine("释放安全Mutex");
}
else
{
Console.WriteLine("获取安全Mutex超时");
}
}
}
catch (Exception ex)
{
Console.WriteLine($"安全Mutex操作失败: {ex.Message}");
}
}
static void DemonstrateTimeoutHandling()
{
Console.WriteLine("\n=== Mutex超时处理 ===");
using (var mutex = new Mutex(false, "Local\\TimeoutMutexExample"))
{
// 启动一个长时间持有互斥量的任务
Task longTask = Task.Run(() =>
{
try
{
Console.WriteLine("长任务: 获取互斥量...");
mutex.WaitOne();
Console.WriteLine("长任务: 开始长时间工作...");
Thread.Sleep(5000);
Console.WriteLine("长任务: 工作完成");
}
finally
{
mutex.ReleaseMutex();
Console.WriteLine("长任务: 释放互斥量");
}
});
Thread.Sleep(100); // 确保长任务先获得互斥量
// 启动多个尝试获取互斥量的任务
Task[] shortTasks = new Task[3];
for (int i = 0; i < shortTasks.Length; i++)
{
int taskId = i;
shortTasks[i] = Task.Run(() =>
{
Console.WriteLine($"短任务{taskId}: 尝试获取互斥量(2秒超时)...");
if (mutex.WaitOne(2000))
{
try
{
Console.WriteLine($"短任务{taskId}: 成功获得互斥量");
Thread.Sleep(500);
}
finally
{
mutex.ReleaseMutex();
Console.WriteLine($"短任务{taskId}: 释放互斥量");
}
}
else
{
Console.WriteLine($"短任务{taskId}: 获取互斥量超时,执行替代方案");
// 执行不需要互斥量的操作
Thread.Sleep(500);
Console.WriteLine($"短任务{taskId}: 替代方案执行完成");
}
});
}
Task.WaitAll(new[] { longTask }.Concat(shortTasks).ToArray());
}
}
}
案例4:Mutex错误处理和恢复
using System;
using System.Threading;
using System.Threading.Tasks;
class MutexErrorHandling
{
private static readonly string MutexName = "Global\\ErrorHandlingMutex";
static void Main(string[] args)
{
Console.WriteLine("Mutex错误处理示例");
// 演示放弃的互斥量处理
DemonstrateAbandonedMutex();
Console.WriteLine("按任意键退出...");
Console.ReadKey();
}
static void DemonstrateAbandonedMutex()
{
Console.WriteLine("\n=== 放弃互斥量处理 ===");
// 创建一个任务,故意不释放互斥量
Task abandoningTask = Task.Run(() =>
{
try
{
using (var mutex = new Mutex(false, MutexName))
{
Console.WriteLine("放弃任务: 获取互斥量...");
mutex.WaitOne();
Console.WriteLine("放弃任务: 获得互斥量,模拟崩溃...");
// 模拟应用程序崩溃或异常退出
// 故意不调用ReleaseMutex()
Thread.Sleep(2000);
Console.WriteLine("放弃任务: 异常退出(不释放互斥量)");
// 这里我们故意抛出异常来模拟崩溃
throw new Exception("模拟应用程序崩溃");
}
}
catch (Exception ex)
{
Console.WriteLine($"放弃任务异常: {ex.Message}");
// 注意:由于异常,ReleaseMutex()不会被调用
}
});
// 等待放弃任务完成
try
{
abandoningTask.Wait();
}
catch
{
Console.WriteLine("放弃任务已异常退出");
}
Thread.Sleep(1000);
// 现在尝试获取被放弃的互斥量
Task recoveryTask = Task.Run(() =>
{
try
{
using (var mutex = new Mutex(false, MutexName))
{
Console.WriteLine("恢复任务: 尝试获取互斥量...");
mutex.WaitOne(5000); // 这里会抛出AbandonedMutexException
Console.WriteLine("恢复任务: 成功获得互斥量并继续工作");
Thread.Sleep(1000);
mutex.ReleaseMutex();
Console.WriteLine("恢复任务: 正常释放互斥量");
}
}
catch (AbandonedMutexException ex)
{
Console.WriteLine($"恢复任务: 检测到被放弃的互斥量,已自动获得所有权");
Console.WriteLine($"异常信息: {ex.Message}");
// 即使捕获了AbandonedMutexException,我们仍然拥有互斥量
// 需要正确释放它
try
{
using (var mutex = new Mutex(false, MutexName))
{
Console.WriteLine("恢复任务: 清理被放弃的互斥量");
Thread.Sleep(1000);
mutex.ReleaseMutex();
Console.WriteLine("恢复任务: 成功清理互斥量");
}
}
catch (Exception cleanupEx)
{
Console.WriteLine($"清理失败: {cleanupEx.Message}");
}
}
catch (Exception ex)
{
Console.WriteLine($"恢复任务其他异常: {ex.Message}");
}
});
recoveryTask.Wait();
}
}
知识点总结
-
Mutex的核心特性:
- 跨进程同步能力
- 命名机制支持进程间识别
- 所有权概念确保安全释放
- 系统级资源管理
-
最佳实践:
- 总是使用using语句确保资源释放
- 设置合理的超时时间
- 处理AbandonedMutexException
- 考虑安全描述符设置
-
性能考虑:
- Mutex比Lock慢,但功能更强大
- 跨进程同步有额外开销
- 适用于需要跨进程同步的场景
-
错误处理:
- 处理超时情况
- 检测和恢复被放弃的互斥量
- 提供备用方案
-
使用场景:
- 单实例应用程序
- 进程间资源保护
- 分布式系统协调
- 跨进程临界区保护

浙公网安备 33010602011771号