C# 多线程:Mutex的高效应用与实战技巧

112 阅读4分钟

前言

在多线程编程中,线程同步是一个关键问题。Mutex(互斥锁)是一种用于确保多个线程在同一时间内只能访问共享资源的同步机制。与Monitor或lock相比,Mutex可以跨线程和跨进程同步,提供了更广泛的适用场景。

Mutex 的特点

跨进程同步:Mutex 允许在不同进程之间进行同步,这使得它在处理需要进程间通信的场景时相当有用。

独占性访问:只有一个线程可以获得 Mutex,其他线程必须等待,直到该线程释放 Mutex

超时控制:Mutex 允许设置超时。如果线程在指定时间内无法获得锁,可以选择放弃。

简单易用:Mutex 提供了清晰的接口,易于程序员使用。

使用 Mutex 的基本语法

以下是 Mutex 的基本用法示例:

Mutex mutex = new Mutex();
mutex.WaitOne(); //请求锁
// ... 访问共享资源
mutex.ReleaseMutex(); //释放锁

使用 Mutex 实现线程安全的计数器

以下示例展示了如何使用 Mutex 来实现一个线程安全的计数器,确保只有一个线程可以对计数器进行更新。

using System;
using System.Threading;

class Program {
    private static int counter = 0; // 共享资源
    private static Mutex mutex = 
    new Mutex(); 
    // 创建 Mutex 实例

    static void Main(string[] args) {
        Thread[] threads =
        new Thread[5];
        for (int i = 0; i < threads.Length; i++) {
            threads[i] = 
            new Thread(IncrementCounter);
            threads[i].Start();
        }

        foreach (var thread in threads) {
            thread.Join();
        }

        Console.WriteLine($"最终计数器的值: {counter}");
    }

    static void IncrementCounter() {
        for (int i = 0; i < 1000; i++) {
            mutex.WaitOne(); 
            // 请求锁
            try {
                counter++; 
                // 增加计数
            } finally {
                mutex.ReleaseMutex(); 
                // 确保释放锁
            }
        }
    }
}

使用命名Mutex进行跨进程同步

当需要在不同进程中共享资源时,可以使用命名的Mute。

以下示例展示了如何创建一个命名的Mute。

using System;
using System.Threading;

class Program {
    private static Mutex mutex;

    static void Main(string[] args) {
        string mutexName = 
        "Global\\MyNamedMutex"; // 使用命名 mutex
        mutex = new Mutex(false, mutexName); 
        // 创建一个命名的 mutex

        Thread thread1 = 
        new Thread(AccessResource);
        Thread thread2 = 
        new Thread(AccessResource);

        thread1.Start();
        thread2.Start();

        thread1.Join();
        thread2.Join();
    }

    static void AccessResource() {
        Console.WriteLine
        ($"{Thread.CurrentThread.ManagedThreadId} 正在请求锁...");
        mutex.WaitOne(); // 请求锁
        Console.WriteLine
        ($"{Thread.CurrentThread.ManagedThreadId} 获得了锁。");

        // 模拟访问共享资源的操作
        Thread.Sleep(2000); 
        // 休眠2秒以模拟长时间的操作

        Console.WriteLine
        ($"{Thread.CurrentThread.ManagedThreadId} 释放了锁。");
        mutex.ReleaseMutex(); // 释放锁
    }
}

使用超时功能

在某些情况下,锁请求可能会超时,以下示例展示了如何使用 Mutex 的超时功能。

namespace AppMutex
{
    internal class Program
    {
        private static Mutex mutex = new Mutex();

        static void Main(string[] args)
        {
            Thread thread1 = 
            new Thread(EnterCriticalSection);
            Thread thread2 = 
            new Thread(EnterCriticalSection);

            thread1.Start();
            thread2.Start();

            thread1.Join();
            thread2.Join();
        }

        static void EnterCriticalSection()
        {
            Console.WriteLine
            ($"{Thread.CurrentThread.ManagedThreadId} 正在请求锁...");
            if (mutex.WaitOne(1000))
            { 
                // 请求锁,最多等待1秒
                try
                {
                    Console.WriteLine
                    ($"{Thread.CurrentThread.ManagedThreadId} 获得了锁。");
                    // 模拟长时间运行的操作
                    Thread.Sleep(3000); 
                    // 超过1秒的操作
                }
                finally
                {
                    mutex.ReleaseMutex();
                    Console.WriteLine
                    ($"{Thread.CurrentThread.ManagedThreadId} 释放了锁。");
                }
            }
            else
            {
                Console.WriteLine
                ($"{Thread.CurrentThread.ManagedThreadId} 等待超时,未能获得锁。");
            }
        }
    }
}

1、超时请求:在获取 Mutex 锁时,设置了超时时间为1秒。

2、超时处理:如果在1秒内无法获得锁,则线程输出等待超时的信息。

3、将超时修改为4秒,可以正常释放了。

单实例应用程序

namespace AppMutex
{
   internal class Program
    {
        static Mutex mutex = new Mutex(true, "MyAPPID");

        static void Main(string[] args)
        {
            // 尝试获取互斥锁  
            if (mutex.WaitOne(TimeSpan.Zero, true))
            {
                try
                {
                    // 应用程序的主逻辑  
                    Console.WriteLine("应用程序正在运行");
                    // 其他业务代码  
                }
                finally
                {
                    // 释放互斥锁  
                    mutex.ReleaseMutex();
                }
            }
            else
            {
                Console.WriteLine("应用程序已经在运行");
                return;
            }

            Console.ReadKey();
        }
    }
}

使用 Mutex 的注意事项

性能问题:Mutex的性能通常低于Monitor或lock,因为它涉及操作系统级别的调用。在性能敏感的应用中,应谨慎使用。

死锁风险:使用Mutex时,必须确保在所有路径上正确释放互斥锁,否则可能导致死锁。

异常处理:在使用Mutex时,建议在try-finally块中释放互斥锁,以确保即使发生异常也能正确释放资源。

总结

Mutex是C#中用于多线程和跨进程同步的重要工具。通过合理使用Mutex,可以确保多个线程或进程在同一时间内只能访问共享资源,从而避免数据竞争和线程安全问题。然而,Mutex的使用需要注意性能和死锁问题。在实际开发中,应根据具体需求选择合适的同步机制,确保程序的高效和稳定运行。

最后

如果你觉得这篇文章对你有帮助,不妨点个赞支持一下!你的支持是我继续分享知识的动力。如果有任何疑问或需要进一步的帮助,欢迎随时留言。

也可以加入微信公众号 [DotNet技术匠] 社区,与其他热爱技术的同行一起交流心得,共同成长!

优秀是一种习惯,欢迎大家留言学习!

作者:技术老小子

出处:mp.weixin.qq.com/s/3ndip3dHvQYVth71jfslwg

声明:网络内容,仅供学习,尊重版权,侵权速删,歉意致谢!