08、使用互斥量在进程间进行同步

使用互斥量在进程间进行同步

知识点

什么是Mutex

Mutex(互斥量)是一种同步原语,不仅可以在同一进程的线程间进行同步,还可以在不同进程间进行同步。Mutex确保同一时间只有一个线程或进程可以访问共享资源。

Mutex的特点

  1. 跨进程同步:可以在不同进程间共享
  2. 命名支持:支持命名Mutex,便于进程间识别
  3. 所有权概念:只有获得Mutex的线程才能释放它
  4. 递归支持:同一线程可以多次获得同一个Mutex
  5. 系统级资源:由操作系统管理

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();
    }
}

知识点总结

  1. Mutex的核心特性:

    • 跨进程同步能力
    • 命名机制支持进程间识别
    • 所有权概念确保安全释放
    • 系统级资源管理
  2. 最佳实践:

    • 总是使用using语句确保资源释放
    • 设置合理的超时时间
    • 处理AbandonedMutexException
    • 考虑安全描述符设置
  3. 性能考虑:

    • Mutex比Lock慢,但功能更强大
    • 跨进程同步有额外开销
    • 适用于需要跨进程同步的场景
  4. 错误处理:

    • 处理超时情况
    • 检测和恢复被放弃的互斥量
    • 提供备用方案
  5. 使用场景:

    • 单实例应用程序
    • 进程间资源保护
    • 分布式系统协调
    • 跨进程临界区保护
posted @ 2025-08-27 01:24  jessqiu  阅读(63)  评论(0)    收藏  举报