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

释放互斥锁的所有权


相关推荐
工业甲酰苯胺6 分钟前
聊一聊 C#线程池 的线程动态注入
java·开发语言·c#
zfenggo7 分钟前
c/c++ 无法跳转定义
c语言·开发语言·c++
向宇it17 分钟前
【从零开始入门unity游戏开发之——C#篇30】C#常用泛型数据结构类——list<T>列表、`List<T>` 和数组 (`T[]`) 的选择
java·开发语言·数据结构·unity·c#·游戏引擎·list
hakesashou22 分钟前
python怎么看矩阵维数
开发语言·python
daopuyun31 分钟前
GB/T34944-2017 《Java语言源代码漏洞测试规范》解读——安全功能
java·开发语言·安全
qh0526wy42 分钟前
pyqt5冻结+分页表
开发语言·python·qt
hjxxlsx1 小时前
探索 C++ 自定义函数的深度与广度
开发语言·c++
罗政1 小时前
PDF书籍《手写调用链监控APM系统-Java版》第12章 结束
java·开发语言·pdf
匹马夕阳1 小时前
详细对比JS中XMLHttpRequest和fetch的使用
开发语言·javascript·ecmascript
月巴月巴白勺合鸟月半1 小时前
一个特别的串口通讯
开发语言·串口通讯