作用
当线程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()
释放互斥锁的所有权