通过实例学C#之Mutex(互斥锁)类

作用

当线程A需要对数据1进行判断,然后根据判断结果进行后续操作时,如果线程B,此刻也对数据1进行操作,可能会产生意向不到的错误。

例如,设备J对工件X的温度进行判断,得知工件的温度满足条件后,可以进行下一个工艺操作,此时,如果另一个设备K对工件X进行降温操作,那么,工件X的温度就不满足执行后续制作工艺条件,这时候,如果强行进行后续操作,可能会产生次品。

那么,中间出现问题的步骤是在哪里呢?很明显,当设备J对工件X进行温度判断后,直到后面工艺执行完,按理说都不能让其他设备对工件X进行温度上的控制。而互斥锁,就可以把工件X锁住,此时,所有其他设备都无法改变其温度,等执行完后续工艺后,再解锁,然后其他设备就可以对工件X的温度进行控制了。


构造函数

Mutex()

创建一个Mutex互斥锁对象,且当前线程对互斥锁无所有权,如果需要获取对互斥锁的所有权,需要使用WaitOne()方法。

不使用互斥锁实例:

cs 复制代码
static int count = 0;        //用于计数

static void Main(string[] args)
{
    Thread t1 = new Thread(ShowValue);
    Thread t2= new Thread(ChangeValue);

    t1.Start();     //线程1启动
    t2.Start();     //线程2启动

    Console.WriteLine("主线程结束,请按任意键退出");
    Console.ReadKey();
}

//线程1方法
static void ShowValue()
{
    for (int i = 0; i < 5; i++)
    {
        Console.WriteLine("t1:count值为:" + count.ToString());
        Thread.Sleep(1000);
    }
}

//线程2方法
static void ChangeValue()
{
    for (int i = 0; i < 5; i++)
    {
        Console.WriteLine("-------t2--------对count进行+1操作");
        count++;
        Console.WriteLine("-------t2--------count值为:" + count.ToString());
        Thread.Sleep(1000);
    }
}

运行结果:

cs 复制代码
主线程结束,请按任意键退出
t1:count值为:0
-------t2--------对count进行+1操作
-------t2--------count值为:1
-------t2--------对count进行+1操作
-------t2--------count值为:2
t1:count值为:1
t1:count值为:2
-------t2--------对count进行+1操作
-------t2--------count值为:3
-------t2--------对count进行+1操作
-------t2--------count值为:4
t1:count值为:3
-------t2--------对count进行+1操作
-------t2--------count值为:5
t1:count值为:4

可以看到,子线程的操作对主线程的读数有影响。


如果希望主线程执行过程中,count保持不变,就需要使用互斥锁Mutex对象。实例如下:

cs 复制代码
static int count = 0;        //用于计数
static Mutex mutex = new Mutex();      //创建互斥锁实例
static void Main(string[] args)
{
    Thread t1 = new Thread(ShowValue);
    Thread t2= new Thread(ChangeValue);
    
    t1.Start();     //线程1启动
    t2.Start();     //线程2启动

    Console.WriteLine("主线程结束,请按任意键退出");
    Console.ReadKey();
}

//线程1方法
static void ShowValue()
{
    mutex.WaitOne();        //如果互斥锁的所有权被释放,那么线程1获取互斥锁的所有权
    for (int i = 0; i < 5; i++)
    {
        Console.WriteLine("t1:count值为:" + count.ToString());
        Thread.Sleep(1000);
    }
    mutex.ReleaseMutex();        //线程1释放互斥锁
}

//线程2方法
 static void ChangeValue()
 {
     mutex.WaitOne();        //如果互斥锁的所有权被释放,那么线程2获取互斥锁的所有权
     for (int i = 0; i < 5; i++)
     {
         Console.WriteLine("-------t2--------对count进行+1操作");
         count++;
         Console.WriteLine("-------t2--------count值为:" + count.ToString());
         Thread.Sleep(1000);
     }
     mutex.ReleaseMutex();        //线程2释放互斥锁
 }

运行结果:

cs 复制代码
主线程结束,请按任意键退出
t1:count值为:0
t1:count值为:0
t1:count值为:0
t1:count值为:0
t1:count值为:0
-------t2--------对count进行+1操作
-------t2--------count值为:1
-------t2--------对count进行+1操作
-------t2--------count值为:2
-------t2--------对count进行+1操作
-------t2--------count值为:3
-------t2--------对count进行+1操作
-------t2--------count值为:4
-------t2--------对count进行+1操作
-------t2--------count值为:5

可以看到,由于T1先启动,所以在使用mutex.WaitOne()方法后,T1获得互斥锁mutex的所有权,这时候,T2的mutex.WaitOne()代码返回false,所以线程阻塞,必须等T1调用mutex.ReleaseMutex()后才能继续执行。


子线程的代码不变,如果我们更改一下T1,T2的启动顺序:

cs 复制代码
 static void Main(string[] args)
 {
     Thread t2= new Thread(ChangeValue);
     Thread t1 = new Thread(ShowValue);

     t2.Start();     //线程2启动
     t1.Start();     //线程1启动

     Console.WriteLine("主线程结束,请按任意键退出");
     Console.ReadKey();
 }

返回结果:

cs 复制代码
主线程结束,请按任意键退出
-------t2--------对count进行+1操作
-------t2--------count值为:1
-------t2--------对count进行+1操作
-------t2--------count值为:2
-------t2--------对count进行+1操作
-------t2--------count值为:3
-------t2--------对count进行+1操作
-------t2--------count值为:4
-------t2--------对count进行+1操作
-------t2--------count值为:5
t1:count值为:5
t1:count值为:5
t1:count值为:5
t1:count值为:5
t1:count值为:5

可以看到,程序会先执行T2的代码段,然后在T2释放互斥锁以后,T1接着执行。


另外,互斥锁针对的是WaitOne()以及ReleaseMutex()之间的代码,而不是整个线程,Main()函数不变,我们看看下面实例:

cs 复制代码
static void ChangeValue()
{
    Console.WriteLine("T2在互斥锁以外的代码");
    mutex.WaitOne();
    for (int i = 0; i < 5; i++)
    {
        Console.WriteLine("-------t2--------对count进行+1操作");
        count++;
        Console.WriteLine("-------t2--------count值为:" + count.ToString());
        Thread.Sleep(1000);
    }
    mutex.ReleaseMutex();
}

static void ShowValue()
{
    Console.WriteLine("T1在互斥锁以外的代码");
    mutex.WaitOne();
    for (int i = 0; i < 5; i++)
    {
        Console.WriteLine("t1:count值为:" + count.ToString());
        Thread.Sleep(1000);
    }
    mutex.ReleaseMutex();
}

运行结果:

cs 复制代码
主线程结束,请按任意键退出
T1在互斥锁以外的代码
T2在互斥锁以外的代码
t1:count值为:0
t1:count值为:0
t1:count值为:0
t1:count值为:0
t1:count值为:0
-------t2--------对count进行+1操作
-------t2--------count值为:1
-------t2--------对count进行+1操作
-------t2--------count值为:2
-------t2--------对count进行+1操作
-------t2--------count值为:3
-------t2--------对count进行+1操作
-------t2--------count值为:4
-------t2--------对count进行+1操作
-------t2--------count值为:5

可以看到,在互斥锁以外的代码,是不受互斥锁的阻塞影响的。


Mutex(bool)

如果创建实例时,填入一个bool参数,那么表示,在创建该互斥锁的同时,会相应控制互斥锁的所有权,如果参数为True,表示创建互斥锁的线程,具有互斥锁的所有权,如果它不使用ReleaseMux()释放互斥锁的所有权,那么其他使用互斥锁的线程,将一直被阻塞。实例如下

cs 复制代码
static int count = 0;        //用于计数
static Mutex mutex = new Mutex(true);      //创建互斥锁实例并获取互斥锁所有权
static void Main(string[] args)
{
    Thread t2= new Thread(ChangeValue);
    Thread t1 = new Thread(ShowValue);

    
    t2.Start();     //线程2启动
    t1.Start();     //线程1启动

    Console.WriteLine("主线程结束,请按任意键退出");
    Console.ReadKey();
}

线程调用方法不变,运行结果:

cs 复制代码
主线程结束,请按任意键退出

由于主线程在创建时,获取了互斥锁的所有权,而主线程一直没有调用ReleaseMutex()方法,所以,同样使用了互斥锁的T1,T2一直无法执行。


这时候,如果我们在主线程的某一时刻释放了互斥锁:

cs 复制代码
static int count = 0;        //用于计数
static Mutex mutex = new Mutex(true);      //创建互斥锁实例并获取互斥锁所有权
static void Main(string[] args)
{
    Thread t2= new Thread(ChangeValue);
    Thread t1 = new Thread(ShowValue);

    
    t2.Start();     //线程2启动
    t1.Start();     //线程1启动

    Console.WriteLine("主线程延迟3秒释放互斥锁");
    Thread.Sleep(3000);
    mutex.ReleaseMutex();

    Console.WriteLine("主线程结束,请按任意键退出");
    Console.ReadKey();
}

运行结果:

cs 复制代码
主线程延迟3秒释放互斥锁
主线程结束,请按任意键退出
-------t2--------对count进行+1操作
-------t2--------count值为:1
-------t2--------对count进行+1操作
-------t2--------count值为:2
-------t2--------对count进行+1操作
-------t2--------count值为:3
-------t2--------对count进行+1操作
-------t2--------count值为:4
-------t2--------对count进行+1操作
-------t2--------count值为:5
t1:count值为:5
t1:count值为:5
t1:count值为:5
t1:count值为:5
t1:count值为:5

可以看到,主线程释放互斥锁以后,两个分线程都能正常运行。

所以,Mutex mutex=Mutex(True)等价于 Mutex mutex=Mutex()+ mutex.WaitOne()。


常用方法

WaitOne()

获取互斥锁的所有权,如果互斥锁当前处于释放状态,执行WaitOne()后返回true,当前线程获取互斥锁所有权。如果互斥锁被其他线程占有,那么当前线程执行WaitOne()后返回false,获取互斥锁所有权失败,要继续等待拥有互斥锁的线程释放互斥锁所有权。


ReleaseMutex()

释放互斥锁的所有权


相关推荐
刘欣的博客23 分钟前
C# 发送飞书webhook消息
c#·飞书·发消息
free-elcmacom1 小时前
C++ 默认参数详解:用法、规则与避坑指南
开发语言·c++
码云数智-大飞1 小时前
分布式事务解决方案全景指南:2PC、TCC、SAGA 与 Seata 实战
开发语言
娇娇yyyyyy1 小时前
QT编程(10): QLineEdit
开发语言·qt
Albert Edison1 小时前
【ProtoBuf 语法详解】Any 类型
服务器·开发语言·c++·protobuf
喵叔哟2 小时前
5. 【Blazor全栈开发实战指南】--Blazor组件基础
开发语言·javascript·ecmascript
海奥华22 小时前
Rust初步学习
开发语言·学习·rust
卢锡荣2 小时前
LDR6021Q 车规级 Type‑C PD 控制芯片:一芯赋能,边充边传,稳驭全场景
c语言·开发语言·ios·计算机外设·电脑
、BeYourself2 小时前
Scala 基础语法
开发语言·scala
AMoon丶2 小时前
C++模版-函数模版,类模版基础
java·linux·c语言·开发语言·jvm·c++·算法