通过实例学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()

释放互斥锁的所有权


相关推荐
Larry_Yanan7 小时前
Qt多进程(三)QLocalSocket
开发语言·c++·qt·ui
醒过来摸鱼7 小时前
Java classloader
java·开发语言·python
superman超哥7 小时前
仓颉语言中元组的使用:深度剖析与工程实践
c语言·开发语言·c++·python·仓颉
小鸡吃米…7 小时前
Python - 继承
开发语言·python
JIngJaneIL8 小时前
基于java+ vue农产投入线上管理系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot
祁思妙想8 小时前
Python中的FastAPI框架的设计特点和性能优势
开发语言·python·fastapi
唐装鼠8 小时前
rust自动调用Deref(deepseek)
开发语言·算法·rust
Lucas555555558 小时前
现代C++四十不惑:AI时代系统软件的基石与新征程
开发语言·c++·人工智能
源代码•宸8 小时前
goframe框架签到系统项目(BITFIELD 命令详解、Redis Key 设计、goframe 框架教程、安装MySQL)
开发语言·数据库·经验分享·redis·后端·mysql·golang
吃喝不愁霸王餐APP开发者8 小时前
Java后端系统对接第三方外卖API时的幂等性设计与重试策略实践
java·开发语言