C#笔记13 线程同步概念及其实现,详解lock,Monitor,Mutex代码用法

同步的概念

在我们学会在C#中使用线程之后,我们拥有了把一个程序中的不同代码段在不同线程中运行的能力,可以说此时我们已经能够做到让他们分别执行,异步执行。

对于我们的桌面端程序,使用多线程可以让我们在后台进行操作的时候保持用户界面的响应。

对于服务器应用程序,多线程可以让我们单独接收处理每个客户端发来的请求。否则在完全处理完一个请求之前将无法响应其他请求。

但是多线程完全异步意味着线程之间不存在互相配合,此时就必须协调资源的分配和管理,互相之间必须要有协调。

如果多个线程同时访问修改同一个数据,很可能造成数据损坏。

这在数据库程序里的读写操作需要保障安全有异曲同工之处。

线程同步:

是指并发线程,高效有序的访问共享资源的技术。

同步:

同步是指某一时刻,只有一个线程能访问资源,只有当资源所有者放弃了资源或者代码的占有,其他线程才能使用这些资源。

前台线程和后台线程的区别

前台线程只要没有结束,应用程序就不会结束,如果前台线程全部结束,后台线程将自动结束,而不考虑是否完成。

后台线程的属性IsBackgroud需要被设置为True。

 实现方式

lock关键字

lock关键字用于确保代码块同一时间只会有一个线程在运行,不会被其他线程中断。

它的使用形式为:以lock关键字开头,有一个作为参数的对象(引用类型数据),后跟一个代码块。此代码块一次只能由一个线程执行。

using System;
using System.Threading;

class Program
{
    private static int counter = 0;
    private static readonly object lockObject = new object();

    static void Main()
    {
        Thread thread1 = new Thread(IncrementCounter);
        Thread thread2 = new Thread(IncrementCounter);
        
        thread1.Start();
        thread2.Start();
        
        thread1.Join();
        thread2.Join();
        
        Console.WriteLine($"最终计数器值: {counter}");
    }

    static void IncrementCounter()
    {
        for (int i = 0; i < 100000; i++)
        {
            // 使用 lock 来确保此代码块在任意时刻只能由一个线程执行
            lock (lockObject)
            {
                counter++;
            }
        }
    }
}

lock后的参数可以是任意引用类型的对象,但是通常我们用来表示需要进行进程同步的资源,如果一个资源将被多个线程使用,使用对该资源的引用作为lock参数,只要在lock后的代码块内使用该资源,就会是线程安全的,因为这保证了一时间只有一个线程访问该资源。

注意:如果锁定的对象被其他不受我们控制的同步语句锁定,那么就会导致死锁,尤其是public公共类型或者字符串这类会被公共语言库暂留,只要有任意一个线程锁定了这个字符串,就会锁定所有以此字符串为锁的代码块。锁定这类不受我们控制的对象实例,这是非常危险的。

最好锁定:不会被暂留的私有或者受保护成员。

  • 简单易用:使用 lock 可以快速实现线程同步。
  • 自动化异常处理lock 结构会自动释放锁,即使在发生异常时,也能够保证锁被正确释放。
  • 对象锁lock 需要一个引用类型的对象作为锁标识,这个对象可以是任何引用类型的实例。

 理解上很简单,这个代码块有一把代表了资源的锁,这把锁如果我们进入了代码块(使用了资源),就会锁上。这样其他线程就进不来了,同样的,如果选的锁不好,就会导致很多代码块等一个没必要等的锁(资源)。

lock本质是由下面的Monitor实现的。

Monitor类

此类提供了同步对象的访问机制。

它可以向一个线程授予对象锁用来控制对对象的访问。对象锁提供限制访问代码块的能力。当一个线程获得了这个锁之后,其他线程不能获得该锁。

功能:

  • 与某个对象关联
  • 未绑定的,可以从任何上下文直接调用它。
  • 不能创建该类实例

方法

Enter:请求获取锁。
Exit:释放锁。
Wait:释放锁并阻塞当前线程,直到接收到另一个线程的通知。
Pulse:通知正在等待的线程它们可以继续。
PulseAll:通知所有等待的线程它们可以继续。

using System;
using System.Threading;

class Program
{
    private static readonly object lockObject = new object(); // 任意对象,用于锁定

    static void Main()
    {
        Thread thread1 = new Thread(DoWork);
        Thread thread2 = new Thread(DoWork);

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

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

        Console.WriteLine("所有线程已完成工作。");
    }

    static void DoWork()
    {
        // 使用 Monitor 的未绑定方法
        Monitor.Enter(lockObject); // 这里直接调用静态方法 Enter
        try
        {
            // 临界区代码
            Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId} 正在工作...");
            Thread.Sleep(1000); // 模拟一些工作
        }
        finally
        {
            Monitor.Exit(lockObject); // 确保在任何情况下都释放锁
        }
    }
}

用Monitor模拟售票系统

using System;
using System.Threading;

class Program
{
    private static int totalTickets = 10; // 共享资源:总票数
    private static readonly object lockObject = new object(); // 用于同步的锁对象

    static void Main()
    {
        // 创建四个线程模拟购票
        Thread thread1 = new Thread(BuyTicket);
        Thread thread2 = new Thread(BuyTicket);
        Thread thread3 = new Thread(BuyTicket);
        Thread thread4 = new Thread(BuyTicket);

        // 启动线程
        thread1.Start();
        thread2.Start();
        thread3.Start();
        thread4.Start();

        // 等待所有线程完成
        thread1.Join();
        thread2.Join();
        thread3.Join();
        thread4.Join();

        Console.WriteLine("所有线程已完成购票。");
    }

    static void BuyTicket()
    {
        while (true)
        {
            // 使用 Monitor 进行同步
            Monitor.Enter(lockObject);
            try
            {
                if (totalTickets > 0)
                {
                    // 模拟购票过程
                    Console.WriteLine($"线程 {Thread.CurrentThread.ManagedThreadId} 正在购买第 {totalTickets} 张票...");
                    totalTickets--; // 购买一张票
                    Thread.Sleep(100); // 模拟一些操作的延迟
                }
                else
                {
                    // 票已售完,退出循环
                    Console.WriteLine($"线程 {Thread.CurrentThread.ManagedThreadId} 发现票已售完。");
                    break;
                }
            }
            finally
            {
                // 确保释放锁
                Monitor.Exit(lockObject);
            }
        }
    }
}

标准输出:线程 5 正在购买第 10 张票...
线程 5 正在购买第 9 张票...
线程 5 正在购买第 8 张票...
线程 5 正在购买第 7 张票...
线程 5 正在购买第 6 张票...
线程 5 正在购买第 5 张票...
线程 5 正在购买第 4 张票...
线程 5 正在购买第 3 张票...
线程 5 正在购买第 2 张票...
线程 5 正在购买第 1 张票...
线程 5 发现票已售完。
线程 3 发现票已售完。
线程 6 发现票已售完。
线程 4 发现票已售完。
所有线程已完成购票。

 作为一种锁机制,最好不要锁定公开的或者会被暂留的字符串。

锁定当前的对象实例也是不可靠的,因为可能其他代码只要获得该对象实例的引用就可以锁定这个实例。

Monitor.enter(this);

 wait方法:

拥有锁的线程放弃资源并选择等待,可以被下面的通知唤醒。

Monitor.Enter(lockObject);
try
{
    while (conditionNotMet)
    {
        Monitor.Wait(lockObject); // 释放锁并等待通知
    }
    // 执行相关操作
}
finally
{
    Monitor.Exit(lockObject); // 确保释放锁
}

pulse方法

唤醒一个在等待此资源的线程。

唤醒的线程不立即获得锁,它会在被唤醒后,重新尝试获取锁。这是因为 Pulse 只是通知一个线程,它仍然需要通过重新获取锁来继续执行。

Monitor.Enter(lockObject);
try
{
    // 执行相关操作
    Monitor.Pulse(lockObject); // 唤醒一个等待的线程
}
finally
{
    Monitor.Exit(lockObject); // 确保释放锁
}

 pulseAll方法

同上,但是唤醒所有。

用于需要通知所有等待线程某个状态变化的场景,比如通知多个消费者,数据已经准备好。

Monitor.Enter(lockObject);
try
{
    // 执行相关操作
    Monitor.PulseAll(lockObject); // 唤醒所有等待的线程
}
finally
{
    Monitor.Exit(lockObject); // 确保释放锁
}

TryEnter方法

public static bool TryEnter(object obj);
public static bool TryEnter(object obj, int millisecondsTimeout);
public static bool TryEnter(object obj, TimeSpan timeout);

 尝试获取对象锁,可以设定在指定时间内获取锁。返回获取的成功与否的布尔值。

具有超时的等待

public static bool Wait(object obj, int millisecondsTimeout);
public static bool Wait(object obj, TimeSpan timeout);

让线程释放对象锁并等待一定时间自动唤醒(不会立刻开始执行,会重新尝试获得锁)。 

Monitor.Enter(lockObject);
try
{
    while (conditionNotMet)
    {
        if (!Monitor.Wait(lockObject, TimeSpan.FromSeconds(5))) // 等待5秒
        {
            Console.WriteLine("等待超时");
            break;
        }
    }
    // 执行相关操作
}
finally
{
    Monitor.Exit(lockObject); // 确保释放锁
}

 

生产消费者模型

using System;
using System.Threading;

class Program
{
    private static readonly object lockObject = new object();
    private static bool dataAvailable = false;

    static void Main()
    {
        Thread producer = new Thread(Produce);
        Thread consumer = new Thread(Consume);

        producer.Start();
        consumer.Start();

        producer.Join();
        consumer.Join();

        Console.WriteLine("生产者-消费者过程完成。");
    }

    static void Produce()
    {
        Monitor.Enter(lockObject);
        try
        {
            Console.WriteLine("生产者:正在生产数据...");
            Thread.Sleep(2000); // 模拟生产过程
            dataAvailable = true;
            Monitor.Pulse(lockObject); // 唤醒等待的消费者线程
            Console.WriteLine("生产者:数据已生产,通知消费者。");
        }
        finally
        {
            Monitor.Exit(lockObject); // 确保释放锁
        }
    }

    static void Consume()
    {
        Monitor.Enter(lockObject);
        try
        {
            while (!dataAvailable)
            {
                Console.WriteLine("消费者:等待数据...");
                bool signaled = Monitor.Wait(lockObject, TimeSpan.FromSeconds(3)); // 等待生产者通知,最多3秒
                if (!signaled)
                {
                    Console.WriteLine("消费者:等待超时,尝试重新获取锁...");
                }
            }

            Console.WriteLine("消费者:获取数据并进行消费。");
        }
        finally
        {
            Monitor.Exit(lockObject); // 确保释放锁
        }
    }
}

 Mutex类

同步基元。基本想法和作用类似于我们的Monitor类,但是它可以实现跨进程的线程同步。

它只向一个线程授予对共享资源的获取权限,如果一个线程获取了互斥体,则其他想要这个互斥体的线程将被挂起。

它的本质是对win32的封装,比Monitor更强大,但是不适合用于进程内的同步,因为这样会浪费一些系统转换的资源。Monitor会更好的利用资源。

基本使用

和Monitor类似,在进程内,使用Mutex实例获取锁,或者释放锁。

这里和Monitor不同的是,这里没有Enter方法来获取锁,就是直接阻塞然后等待锁(资源)。Monitor获取锁之后还能使用Wait方法等待资源,同时让出锁,但是我们这里就只直接释放锁即可。

可以这么理解:Monitor可以获取,可以释放,可以释放在等待重新获取,可以通知其他正在等待这个资源的线程。

Mutex也可以获取,也可以释放,可以释放再等待其他资源(注意是其他资源),可以一次性等待多个资源。

创建 Mutex 对象:

Mutex mutex = new Mutex(); 创建一个新的 Mutex 对象。所有需要同步的线程共享这个 Mutex 实例。
请求获得锁 (WaitOne):

mutex.WaitOne(); 请求当前线程获得 Mutex 锁。如果锁已经被其他线程占用,当前线程将会阻塞,直到锁被释放。
释放锁 (ReleaseMutex):

mutex.ReleaseMutex(); 释放当前线程持有的锁,使其他等待的线程能够获得锁。

using System;
using System.Threading;

class Program
{
    // 创建一个 Mutex 对象
    private static Mutex mutex = new Mutex();

    static void Main()
    {
        // 启动多个线程来模拟并发访问
        for (int i = 0; i < 5; i++)
        {
            Thread thread = new Thread(AccessSharedResource);
            thread.Name = $"线程 {i + 1}";
            thread.Start();
        }
    }

    static void AccessSharedResource()
    {
        Console.WriteLine($"{Thread.CurrentThread.Name} 正在等待进入临界区...");

        // 请求获得 Mutex 锁
        mutex.WaitOne();

        try
        {
            Console.WriteLine($"{Thread.CurrentThread.Name} 已进入临界区。");
            // 模拟对共享资源的访问
            Thread.Sleep(2000);
            Console.WriteLine($"{Thread.CurrentThread.Name} 正在离开临界区。");
        }
        finally
        {
            // 释放 Mutex 锁
            mutex.ReleaseMutex();
            Console.WriteLine($"{Thread.CurrentThread.Name} 已释放锁。");
        }
    }
}

输出结果: 

线程 3 正在等待进入临界区...
线程 1 正在等待进入临界区...
线程 2 正在等待进入临界区...
线程 3 已进入临界区。
线程 4 正在等待进入临界区...
线程 5 正在等待进入临界区...
线程 3 正在离开临界区。
线程 3 已释放锁。
线程 5 已进入临界区。
线程 5 正在离开临界区。
线程 5 已释放锁。
线程 4 已进入临界区。
线程 4 正在离开临界区。
线程 4 已释放锁。
线程 2 已进入临界区。
线程 2 正在离开临界区。
线程 2 已释放锁。
线程 1 已进入临界区。
线程 1 正在离开临界区。
线程 1 已释放锁。

 带超时的Mutex等待锁:

using System;
using System.Threading;

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

    static void Main()
    {
        for (int i = 0; i < 3; i++)
        {
            Thread thread = new Thread(TryAccessSharedResource);
            thread.Name = $"线程 {i + 1}";
            thread.Start();
        }
    }

    static void TryAccessSharedResource()
    {
        Console.WriteLine($"{Thread.CurrentThread.Name} 正在等待进入临界区...");

        // 尝试获取锁,超时时间为 3 秒
        if (mutex.WaitOne(TimeSpan.FromSeconds(3)))
        {
            try
            {
                Console.WriteLine($"{Thread.CurrentThread.Name} 已进入临界区。");
                Thread.Sleep(2000); // 模拟操作
            }
            finally
            {
                mutex.ReleaseMutex();
                Console.WriteLine($"{Thread.CurrentThread.Name} 已释放锁。");
            }
        }
        else
        {
            Console.WriteLine($"{Thread.CurrentThread.Name} 无法在指定时间内获得锁。");
        }
    }
}

上面等待锁的逻辑没有变化,只是尝试获取锁,并指定最大等待时间为 3 秒。如果在 3 秒内没有获取到锁,返回 false,否则返回 true 并继续执行。不会被一直阻塞,会提供bool值以供走到另外一个分支。

跨进程使用Mutex

需要使用命名Mutex才行,跨进程即跨应用程序,所以已经超越了我们命名全局变量等的作用范围。

创建Mutex的进程:

using System;
using System.Threading;

class Program
{
    static void Main()
    {
        // 创建一个命名的 Mutex,用于跨进程同步
        using (Mutex mutex = new Mutex(false, "Global\\MyNamedMutex"))
        {
            Console.WriteLine("等待获取跨进程的 Mutex...");

            // 尝试获取 Mutex 锁
            if (mutex.WaitOne(TimeSpan.FromSeconds(10)))
            {
                try
                {
                    Console.WriteLine("获取到跨进程的 Mutex,正在执行操作...");
                    Thread.Sleep(5000); // 模拟操作
                }
                finally
                {
                    mutex.ReleaseMutex();
                    Console.WriteLine("已释放跨进程的 Mutex。");
                }
            }
            else
            {
                Console.WriteLine("无法在指定时间内获取跨进程的 Mutex。");
            }
        }
    }
}

另外一个使用这个Mutex的进程: 

using System;
using System.Threading;

class Program
{
    static void Main()
    {
        try
        {
            // 打开已经存在的命名 Mutex
            Mutex existingMutex = Mutex.OpenExisting("Global\\MyNamedMutex");
            Console.WriteLine("成功打开已存在的命名 Mutex。");
        }
        catch (WaitHandleCannotBeOpenedException)
        {
            Console.WriteLine("命名 Mutex 不存在。");
        }
    }
}

 

注意事项

开销较大:Mutex 通常比 lock 或 Monitor 开销更大,因为它是内核对象,需要更多的系统资源。仅在需要跨进程同步时才使用 Mutex,否则可以考虑使用 lock 或 Monitor。
防止死锁:确保每次 WaitOne 成功后,使用 ReleaseMutex 释放锁,以防止死锁。
使用 using 语句:在使用命名 Mutex 时,推荐使用 using 语句以确保在异常情况下也能正确释放资源。

Mutex进阶

构造函数

布尔值指示当前线程是否立刻获得此Mutex。string用于指示Mutex命名。

还有一个带有一个输出参数的构造函数,用于输出当前命名的Mutex是否是新的,如果该Mutex已经存在,则返回False,否则为True。

Mutex mutex = new Mutex();
Mutex mutex = new Mutex(true);
Mutex mutex = new Mutex(false, "Global\\MyNamedMutex");


bool createdNew;
Mutex mutex = new Mutex(false, "Global\\MyNamedMutex", out createdNew);
Console.WriteLine($"Mutex 是否新创建: {createdNew}");

WaitHandle是什么 

是Mutex,Semaphore等的基类,定义一种用于同步的抽象。

WaitHandle 的常用方法
WaitOne():阻塞当前线程,直到收到信号或超时。该方法用于等待单个 WaitHandle 变为有信号状态。

WaitAny(WaitHandle[]):阻塞当前线程,直到任何一个提供的 WaitHandle 变为有信号状态。它在第一个 WaitHandle 变为有信号状态时返回。

WaitAll(WaitHandle[]):阻塞当前线程,直到所有提供的 WaitHandle 都变为有信号状态。它在所有 WaitHandle 变为有信号状态后返回。

更多方法:

Close:显式关闭 Mutex,释放与之相关的所有资源。
OpenExisting:打开一个已存在的命名 Mutex,用于进程间同步。
SignalAndWait:释放一个锁,同时等待另一个锁。
WaitAll:等待所有指定的 WaitHandle 对象被释放。
WaitAny:等待任意一个指定的 WaitHandle 对象被释放。
WaitOne:等待一个 Mutex 对象被释放,这是最常用的等待方法。

WaitAll 

和WaitOne一样,要等资源,只不过同时等待所有资源。必须获得所有的锁才会停止阻塞等待。

输出结果: 

线程1 尝试获取 Mutex1...
等待所有的 Mutex 被释放...
线程2 尝试获取 Mutex2...
线程3 尝试获取 Mutex3...
线程1 已获取 Mutex1,等待 3 秒后释放...
线程2 已获取 Mutex2,等待 2 秒后释放...
线程3 已获取 Mutex3,等待 1 秒后释放...
线程3 释放 Mutex3。
线程2 释放 Mutex2。
线程1 释放 Mutex1。
所有的 Mutex 都已被释放。 

using System;
using System.Threading;

class Program
{
    static Mutex mutex1 = new Mutex();
    static Mutex mutex2 = new Mutex();
    static Mutex mutex3 = new Mutex();

    static void Main()
    {
        // 启动线程来模拟不同的 Mutex 状态
        Thread thread1 = new Thread(ThreadProc1);
        Thread thread2 = new Thread(ThreadProc2);
        Thread thread3 = new Thread(ThreadProc3);

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

        // 等待所有的 Mutex 被释放
        Console.WriteLine("等待所有的 Mutex 被释放...");
        bool allSignaled = WaitHandle.WaitAll(new WaitHandle[] { mutex1, mutex2, mutex3 }, 10000); // 等待最多 10 秒

        if (allSignaled)
        {
            Console.WriteLine("所有的 Mutex 都已被释放。");
        }
        else
        {
            Console.WriteLine("等待超时或不是所有的 Mutex 都已被释放。");
        }

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

    static void ThreadProc1()
    {
        Console.WriteLine("线程1 尝试获取 Mutex1...");
        mutex1.WaitOne();
        Console.WriteLine("线程1 已获取 Mutex1,等待 3 秒后释放...");
        Thread.Sleep(3000);
        mutex1.ReleaseMutex();
        Console.WriteLine("线程1 释放 Mutex1。");
    }

    static void ThreadProc2()
    {
        Console.WriteLine("线程2 尝试获取 Mutex2...");
        mutex2.WaitOne();
        Console.WriteLine("线程2 已获取 Mutex2,等待 2 秒后释放...");
        Thread.Sleep(2000);
        mutex2.ReleaseMutex();
        Console.WriteLine("线程2 释放 Mutex2。");
    }

    static void ThreadProc3()
    {
        Console.WriteLine("线程3 尝试获取 Mutex3...");
        mutex3.WaitOne();
        Console.WriteLine("线程3 已获取 Mutex3,等待 1 秒后释放...");
        Thread.Sleep(1000);
        mutex3.ReleaseMutex();
        Console.WriteLine("线程3 释放 Mutex3。");
    }
}

WaitAny

这里主线程等待两个资源,线程1和线程2分别占有一个锁。可以看看输出结果:

等待任意一个 Mutex 被释放...
Mutex 1 已被释放。
线程1 尝试获得 Mutex1...
线程2 尝试获得 Mutex2...
线程2 已获得 Mutex2,等待 3 秒后释放...
线程2 已释放 Mutex2。

using System;
using System.Threading;

class Program
{
    static Mutex mutex1 = new Mutex();
    static Mutex mutex2 = new Mutex();

    static void Main()
    {
        // 启动两个线程,分别尝试获取锁并在稍后释放它们
        Thread thread1 = new Thread(ThreadProc1);
        Thread thread2 = new Thread(ThreadProc2);

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

        // 等待任意一个锁被释放
        Console.WriteLine("等待任意一个 Mutex 被释放...");
        int index = WaitHandle.WaitAny(new WaitHandle[] { mutex1, mutex2 });

        Console.WriteLine($"Mutex {index + 1} 已被释放。");

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

    static void ThreadProc1()
    {
        // 模拟工作
        Thread.Sleep(1000);
        Console.WriteLine("线程1 尝试获得 Mutex1...");
        mutex1.WaitOne();
        Console.WriteLine("线程1 已获得 Mutex1,等待 3 秒后释放...");
        Thread.Sleep(3000);
        mutex1.ReleaseMutex();
        Console.WriteLine("线程1 已释放 Mutex1。");
    }

    static void ThreadProc2()
    {
        // 模拟工作
        Thread.Sleep(2000);
        Console.WriteLine("线程2 尝试获得 Mutex2...");
        mutex2.WaitOne();
        Console.WriteLine("线程2 已获得 Mutex2,等待 3 秒后释放...");
        Thread.Sleep(3000);
        mutex2.ReleaseMutex();
        Console.WriteLine("线程2 已释放 Mutex2。");
    }
}

SignalAndWait

用于占用一该资源之后,释放的同时又获取另外一种资源。

using System;
using System.Threading;

class Program
{
    private static Mutex mutex1 = new Mutex();
    private static Mutex mutex2 = new Mutex();

    static void Main()
    {
        Thread thread1 = new Thread(ThreadProc1);
        Thread thread2 = new Thread(ThreadProc2);

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

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

    static void ThreadProc1()
    {
        mutex1.WaitOne();
        Console.WriteLine("线程1获得了 mutex1");

        // 释放 mutex1 并等待获取 mutex2
        Mutex.SignalAndWait(mutex1, mutex2);
        Console.WriteLine("线程1现在拥有了 mutex2");
    }

    static void ThreadProc2()
    {
        mutex2.WaitOne();
        Console.WriteLine("线程2获得了 mutex2");

        // 释放 mutex2 并等待获取 mutex1
        Mutex.SignalAndWait(mutex2, mutex1);
        Console.WriteLine("线程2现在拥有了 mutex1");
    }
}

 Close

关闭此Mutex实例持有的所有资源。

using System;
using System.Threading;

class Program
{
    static void Main()
    {
        Mutex mutex = new Mutex(false, "MyMutex");

        // 使用 Mutex 进行一些操作
        // ...

        // 显式关闭 Mutex
        mutex.Close();
    }
}

具体例子:

在使用Mutex之后,后面关闭它,此时线程试图获取它或者做任何操作将会报错。

using System;
using System.Threading;

class Program
{
    static void Main()
    {
        // 创建两个 Mutex 对象
        Mutex mutex1 = new Mutex();
        Mutex mutex2 = new Mutex();

        // 线程1尝试获得第一个 Mutex
        Thread thread1 = new Thread(() =>
        {
            Console.WriteLine("线程1 尝试获取 Mutex1...");
            mutex1.WaitOne();
            Console.WriteLine("线程1 已获取 Mutex1。");
            Thread.Sleep(2000); // 模拟一些工作
            Console.WriteLine("线程1 释放 Mutex1。");
            mutex1.ReleaseMutex();
        });

        // 线程2尝试获得第二个 Mutex
        Thread thread2 = new Thread(() =>
        {
            Console.WriteLine("线程2 尝试获取 Mutex2...");
            mutex2.WaitOne();
            Console.WriteLine("线程2 已获取 Mutex2。");
            Thread.Sleep(1000); // 模拟一些工作
            Console.WriteLine("线程2 释放 Mutex2。");
            mutex2.ReleaseMutex();
        });

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

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

        // 关闭 Mutex 对象
        mutex1.Close();
        mutex2.Close();

        Console.WriteLine("Mutex 对象已关闭。");
    }
}

此方法使用后,已经具有Mutex的代码不会停止运行,将会继续执行操作,只是不能再获得它。

到这你已经学会了怎么使用三种方法实现同步,可以的话点个关注支持一下,谢谢你奥。

  • 13
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值