Mutex 类
Mutex 中文为互斥,Mutex 类叫做互斥锁。它还可用于进程间同步的同步基元。
Mutex 跟 lock 相似,但是 Mutex 支持多个进程。Mutex 大约比 lock 慢 20 倍。
互斥锁(Mutex),用于多线程中防止两条线程同时对一个公共资源进行读写的机制。
Windows 操作系统中,Mutex 同步对象有两个状态:
- signaled:未被任何对象拥有;
- nonsignaled:被一个线程拥有;
Mutex 只能在获得锁的线程中,释放锁。
构造函数和方法
Mutex 类其构造函数如下:
Mutex 对于进程同步有所帮助,例如其应用场景主要是控制系统只能运行一个此程序的实例。
Mutex 构造函数中的 String类型参数 叫做互斥量而互斥量是全局的操作系统对象。
Mutex 只要考虑实现进程间的同步,它会耗费比较多的资源,进程内请考虑 Monitor/lock。
Mutex 的常用方法如下:
关于 Mutex 类,我们可以先通过几个示例去了解它。
系统只能运行一个程序的实例
下面是一个示例,用于控制系统只能运行一个此程序的实例,不允许同时启动多次。
class
上面的代码中,有些地方前面没有讲,没关系,我们运行一下生成的程序先。
解释一下上面的示例
Mutex 的工作原理:
当两个或两个以上的线程同时访问共享资源时,操作系统需要一个同步机制来确保每次只有一个线程使用资源。
Mutex 是一种同步基元,Mutex 仅向一个线程授予独占访问共享资源的权限。这个权限依据就是 互斥体,当一个线程获取到互斥体后,其它线程也在试图获取互斥体时,就会被挂起(阻塞),直到第一个线程释放互斥体。
对应我们上一个代码示例中,实例化 Mutex 类的构造函数如下:
new Mutex(
其构造函数原型如下:
public Mutex (bool initiallyOwned, string name, out bool createdNew);
前面我们提出过,Mutex 对象有两种状态,signaled
和 nonsignaled
。
通过 new
来实例化 Mutex 类,会检查系统中此互斥量 name 是否已经被使用,如果没有被使用,则会创建 name
互斥量并且此线程拥有此互斥量的使用权;此时 createdNew == true
。
那么 initiallyOwned
,它的作用是是否允许线程是否能够获取到此互斥量的初始化所有权。因为我们希望只有一个程序能够在后台运行,因此我们要设置为 false。
驱动开发中关于 Mutex :https://docs.microsoft.com/zh-cn/windows-hardware/drivers/kernel/introduction-to-mutex-objects
对了, Mutex 的 参数中,name
是非常有讲究的。
在运行终端服务的服务器上,命名系统 mutex 可以有两个级别的可见性。
- 如果其名称以前缀 "Global" 开头,则 mutex 在所有终端服务器会话中可见。
- 如果其名称以前缀 "Local" 开头,则 mutex 仅在创建它的终端服务器会话中可见。在这种情况下,可以在服务器上的其他每个终端服务器会话中存在具有相同名称的单独 mutex。
- 如果在创建已命名的 mutex 时未指定前缀,则采用前缀 "Local"。在终端服务器会话中,两个互斥体的名称只是它们的前缀不同,它们都是对终端服务器会话中的所有进程都可见。
也就是说,前缀名称 "Global" 和 "Local" 描述互斥体名称相对于终端服务器会话的作用域,而不是相对于进程。
请参考:
- https://docs.microsoft.com/zh-cn/dotnet/api/system.threading.mutex?view=netcore-3.1#methods
- https://www.cnblogs.com/suntp/p/8258488.html
接替运行
这里要实现,当同时点击一个程序时,只能有一个实例A可以运行,其它实例进入等待队列,等待A运行完毕后,然后继续运行队列中的下一个实例。
我们将每个程序比作一个人,模拟一个厕所坑位,每次只能有一个人上厕所,其他人需要排队等候。
使用 WaitOne()
方法来等待别的进程释放互斥量,即模拟排队;ReleaseMutex()
方法解除对坑位的占用。
class
此时,我们使用了
new Mutex(
一个程序结束后,要允许其它线程能够创建 Mutex 对象获取互斥量,需要将构造函数的第一个参数设置为 true
。
你也可以改成 false
,看看会报什么异常。
你可以使用 WaitOne(Int32)
来设置等待时间,单位是毫秒,超过这个时间就不排队了,去别的地方上厕所。
为了避免出现问题,请考虑在 finally
块中执行 m.ReleaseMutex()
。
进程同步示例
这里我们实现一个这样的场景:
父进程 Parent 启动子进程 Children ,等待子进程 Children 执行完毕,子进程退出,父进程退出。
新建一个 .NET Core 控制台项目,名称为 Children,其 Progarm 中的代码如下
using System;
然后发布或生成项目,打开程序文件位置,复制线程文件路径。
创建一个新项目,名为 Parent 的 .NET Core 控制台,其 Program 中的代码如下:
using System;
请将 Children 项目的程序文件路径,替换到 Parent 项目启动子进程的那部分字符串中。
然后启动 Parent.exe,可以观察到如下图的运行过程:
另外
构造函数中,如果为 name
指定 null
或空字符串,则将创建一个本地 Mutex 对象,只会在进程内有效。
Mutex 有些使用方法比较隐晦,可以参考:https://docs.microsoft.com/zh-cn/dotnet/api/system.threading.mutex.-ctor?view=netcore-3.1#System_Threading_Mutex__ctor_System_Boolean_
另外打开互斥体,请参考:
- https://docs.microsoft.com/zh-cn/dotnet/api/system.threading.mutex.openexisting?view=netcore-3.1
- https://docs.microsoft.com/zh-cn/dotnet/api/system.threading.mutex.tryopenexisting?view=netcore-3.1
到目前为止,我们学习了排他锁 lock、Monitor、Mutex。下一篇我们将来学习非排他锁定结构的 Semaphore
和 SemaphoreSlim
。
![aae3b0810376c80c64835013792d8ab8.png](https://img-blog.csdnimg.cn/img_convert/aae3b0810376c80c64835013792d8ab8.png)
![8ccec626d8a695680f2eb1572aea7270.png](https://img-blog.csdnimg.cn/img_convert/8ccec626d8a695680f2eb1572aea7270.png)