ZetCode

C# 互斥锁

最后修改于 2023 年 7 月 5 日

在本文中,我们将展示如何使用 Mutex 原语来确保 C# 中的线程安全。

线程是程序的一个唯一执行路径。Thread 类表示 C# 中的一个线程。它创建和控制线程,设置其优先级并获取其状态。

当一组线程共享相同的资源,并且由于试图访问已被另一个线程锁定的资源而被彼此阻塞时,就会发生死锁

互斥锁

为了确保线程安全,我们需要一种同步机制来授予对共享资源的访问权限。 互斥锁是一种同步原语,它仅授予一个线程访问共享资源的权限。 其他想要访问该资源的线程将被阻塞,直到持有互斥锁的线程释放它。

互斥锁类似于,但它可以跨多个进程工作; 也就是说,它可以是计算机范围的,也可以是应用程序范围的。 Mutex 类位于 System.Threading 命名空间中。

访问共享资源的代码部分称为临界区或保护区。 该资源可以是数据结构或设备。 线程使用 WaitOne 获取互斥锁,并使用 ReleaseMutex 释放互斥锁。

C# 互斥锁简单示例

以下是一个使用互斥锁的简单示例。

Program.cs
class Program
{
    private static Mutex mutex = new Mutex();
    private const int nThreads = 4;

    private static void Worker()
    {
        UseResource();
    }

    private static void UseResource()
    {
        mutex.WaitOne();
        string? name = Thread.CurrentThread.Name;

        Console.WriteLine($"{name} has entered protected section");

        Thread.Sleep(800);
        Console.WriteLine($"{name} has left protected section");

        mutex.ReleaseMutex();
    }

    static void Main(string[] args)
    {
        for (int i = 0; i < nThreads; i++)
        {
            var t = new Thread(new ThreadStart(Worker));
            t.Name = $"Thread {i + 1}";
            t.Start();
        }

        Console.Read();
    }
}

在该程序中,我们创建了四个访问共享资源的线程。 每个线程都具有对资源的独占访问权限。

private static Mutex mutex = new Mutex();

Mutex 是一个静态字段。

private static void Worker()
{
    UseResource();
}

每个线程都调用此 worker 函数。

private static void UseResource()
{
    mutex.WaitOne();
    string? name = Thread.CurrentThread.Name;

    Console.WriteLine($"{name} has entered protected section");

    Thread.Sleep(800);
    Console.WriteLine($"{name} has left protected section");

    mutex.ReleaseMutex();
}

这是代码的临界区,它使用 WaitOneReleaseMutex 方法通过互斥锁进行保护。

for (int i = 0; i < nThreads; i++)
{
    var t = new Thread(new ThreadStart(Worker));
    t.Name = $"Thread {i + 1}";
    t.Start();
}

在一个 for 循环中,我们生成四个线程。

Console.Read();

为了确保所有线程都有时间完成,我们调用 Console.Read 方法。 只需在线程运行完成后按 Enter 键即可。

$ dotnet run
Thread 1 has entered protected section
Thread 1 has left protected section
Thread 2 has entered protected section
Thread 2 has left protected section
Thread 3 has entered protected section
Thread 3 has left protected section
Thread 4 has entered protected section
Thread 4 has left protected section

C# 互斥锁单实例

在以下示例中,我们创建一个单实例应用程序。 只允许在计算机上运行一个应用程序。

要创建单实例应用程序,我们使用命名互斥锁。 为了使互斥锁适用于所有终端会话,互斥锁的名称以 Global\ 为前缀。

Program.cs
public class Program
{
    public static void Main()
    {
        using var mutex = new Mutex(false, @"Global\MySingletonApp");

        bool IsRunning = !mutex.WaitOne(TimeSpan.Zero);

        if (IsRunning)
        {
            Console.WriteLine("application is already running");
            return;
        }

        Console.WriteLine("application started");
        Console.ReadKey();
    
        mutex.ReleaseMutex();
    }
}

在该程序中,我们创建了一个全局命名互斥锁。 这可以防止我们在计算机上创建多个应用程序实例。 只有在正在运行的应用程序完成后才能运行该程序的另一个实例。

尝试在两个不同的终端中运行该应用程序。

来源

Mutex 类 - 语言参考

在本文中,我们使用了 Mutex 原语来同步 C# 中对受保护资源的访问。

作者

我叫 Jan Bodnar,是一位充满热情的程序员,拥有丰富的编程经验。 自 2007 年以来,我一直在撰写编程文章。 迄今为止,我撰写了超过 1,400 篇文章和 8 本电子书。 我拥有超过十年的编程教学经验。

列出所有 C# 教程