Mutex(互斥锁):
Mutex: 命名空间:System.Threading
一个同步基元,也可用于进程间同步。是.NET Framework 中提供跨多个线程,或多个进程同步访问的一个类。它非常类似于Monitor类,他们都只有一个线程能拥有锁定,只有一个线程能获得互斥锁,访问受互斥保护的同步代码区域。
在 Mutex 类的构造函数中,可以指定互斥是否最初应有主线程拥有,定义互斥的名称,获得互斥是否以存在的信息。
例:
bool flag;
Mutex mutex = new Mutex(false, "ProcSharpMutex", out flag);
第三个参数定义输出参数,接受一个互斥是否为新建的 bool 值。如果返回 False 表示互斥已经定义。互斥可以在另一个进程中定义,因为操作系统能够识别有名子的互斥,它有不同的进程共享。如果没有给互斥指定名称,互斥就是未命名的,不在不同的进程之间共享。
要打开已有的互斥,还可以使用 Mutex.OpenExisting()方法,它不需要用构造函数创建互斥时需要的相同 .NET权限。
由于 Mutex 派生于 WaitHandle , 因此可以利用 WaitOne() 方法获得互斥锁定,在该过程中成为该互斥拥有者。调用 ReleaseMutex() 方法 ,即可释放互斥。
例:
if (mutex.WaitOne())
{
try
{
//同步的区域
}
finally
{
mutex.ReleaseMutex();//释放 mutex 一次
}
}
else
{
//等待时发生了一些问题
}
构造函数:
构造函数 | 描述 |
---|---|
Mutex() | 使用默认属性初始化 Mutex 类的新实例。 |
Mutex(Boolean) | 用一个指示调用线程是否应拥有互斥体的初始所属权的布尔值来初始化 Mutex 类的新实例。 |
Mutex(Boolean, String) | 用一个指示调用线程是否应拥有互斥体的初始所属权的布尔值和一个作为互斥体名称的字符串来初始化 Mutex 类的新实例。 |
Mutex(Boolean, String, Boolean) | 使用可指示调用线程是否应具有互斥体的初始所有权以及字符串是否为互斥体的名称的 Boolean 值和当线程返回时可指示调用线程是否已赋予互斥体的初始所有权的 Boolean 值初始化 Mutex 类的新实例。 |
Mutex(Boolean, String, Boolean, MutexSecurity) | 使用可指示调用线程是否应具有互斥体的初始所有权以及字符串是否为互斥体的名称的 Boolean 值和当线程返回时可指示调用线程是否已赋予互斥体的初始所有权以及访问控制安全是否已应用到命名互斥体的 Boolean 变量初始化 Mutex 类的新实例。 |
方法:
方法名称 | 描述 |
---|---|
Close() | 释放由当前 WaitHandle 占用的所有资源。 (Inherited from WaitHandle) |
CreateObjRef(Type) | 创建一个对象,该对象包含生成用于与远程对象进行通信的代理所需的全部相关信息。 (Inherited from MarshalByRefObject) |
Dispose() | 释放 WaitHandle 类的当前实例所使用的所有资源。 (Inherited from WaitHandle) |
Dispose(Boolean) | 当在派生类中重写时,释放 WaitHandle 使用的非托管资源,并且可选择释放托管资源。 (Inherited from WaitHandle) |
Equals(Object) | 确定指定的对象是否等于当前对象。 (Inherited from Object) |
GetAccessControl() | 获取一个 MutexSecurity 对象,该对象表示已命名互斥体的访问控制安全性。 |
OpenExisting(String) | 打开指定名称为 mutex(如果已经存在)。 |
OpenExisting(String, MutexRights) | 用安全访问权限打开指定名称为 mutex(如果已经存在),并返回指示操作是否成功的值。 |
ReleaseMutex() | 释放 Mutex 一次。 |
WaitOne() | 阻止当前线程,直到当前 WaitHandle 收到信号。 |
WaitOne(Int32) | 阻止当前线程,直到当前 WaitHandle 收到信号,同时使用 32 位带符号整数指定时间间隔(以毫秒为单位)。 |
属性:
属性 | 描述 |
---|---|
Handle | 获取或设置本机操作系统句柄。 |
SafeWaitHandle | 获取或设置本机操作系统句柄。 |
字段:
字段 | 描述 |
---|---|
WaitTimeout | 指示在任何等待句柄终止之前 WaitAny(WaitHandle[], Int32, Boolean) 操作已超时。 此字段为常数。 |
详细结构请点击MSDN 文档连接: Mutex
基本用法Dome1:
// 创建一个新的互斥对象。创建线程不拥有互斥对象。
private static Mutex mut = new Mutex();
private const int numIterations = 1;
private const int numThreads = 3;
static void Main(string[] args)
{
// 创建将使用受保护资源的3个线程。
for (int i = 0; i < numThreads; i++)
{
Thread newThread = new Thread(new ThreadStart(ThreadProc));
newThread.Name = String.Format("Thread{0}", i + 1);
newThread.Start();
}
//主线程退出,但是应用程序继续运行,直到所有前台线程退出为止。
Console.ReadKey();
}
private static void ThreadProc()
{
for (int i = 0; i < numIterations; i++)
{
UseResource();
}
}
//此方法表示必须同步的资源
//这样每次只能进入一个线程。
private static void UseResource()
{
// 等安全了再进去。
Console.WriteLine("{0} 正在请求互斥锁", Thread.CurrentThread.Name);
mut.WaitOne();
Console.WriteLine("{0} 已进入保护区", Thread.CurrentThread.Name);
//在这里放置访问不可重入资源的代码。
// 模拟一些工作。
Thread.Sleep(500);
Console.WriteLine("{0} 离开保护区", Thread.CurrentThread.Name);
// 释放互斥锁。
mut.ReleaseMutex();
Console.WriteLine("{0} 正在释放互斥锁", Thread.CurrentThread.Name);
}
输出:每次执行进入的先后顺序都不一样
Dome2: 每个线程调用WaitOne(Int32)方法以获取互斥体。 如果达到超时间隔,该方法返回false,并在线程获取 mutex 既不获得互斥锁保护的资源访问权。 ReleaseMutex只能由获得该互斥体的线程调用方法。
// 创建一个新的互斥对象。创建线程不拥有互斥对象。
private static Mutex mut = new Mutex();
private const int numIterations = 1;
private const int numThreads = 3;
static void Main(string[] args)
{
Program p = new Program();
p.StartThreads();
Console.ReadKey();//这里阻塞一下,可以看到输出结果
}
private void StartThreads()
{
// 创建将使用受保护资源的3个线程。
for (int i = 0; i < numThreads; i++)
{
Thread newThread = new Thread(new ThreadStart(ThreadProc));
newThread.Name = String.Format("Thread{0}", i + 1);
newThread.Start();
}
//主线程退出,但是应用程序继续运行,直到所有前台线程退出为止。
}
private static void ThreadProc()
{
for (int i = 0; i < numIterations; i++)
{
UseResource();
}
}
//此方法表示必须同步的资源
//这样每次只能进入一个线程。
private static void UseResource()
{
// 等安全了再进去。
Console.WriteLine("{0} 正在请求互斥锁", Thread.CurrentThread.Name);
if(mut.WaitOne(1000))
{
Console.WriteLine("{0} 已进入保护区", Thread.CurrentThread.Name);
//在这里放置访问不可重入资源的代码。
// 模拟一些工作。
Thread.Sleep(500);
Console.WriteLine("{0} 离开保护区", Thread.CurrentThread.Name);
// 释放互斥锁。
mut.ReleaseMutex();
Console.WriteLine("{0} 正在释放互斥锁", Thread.CurrentThread.Name);
}
else
{
Console.WriteLine("{0} 没有获取互斥锁", Thread.CurrentThread.Name);
}
}
~Program()
{
mut.Dispose();
}
输出结果:
重要:
此类型实现IDisposable接口。 在使用完类型后,您应直接或间接释放类型。 若要直接释放类型,调用其Dispose中的方法 try / catch块。 若要间接释放类型,请使用 using(在 C# 中)或 Using
可以使用WaitHandle.WaitOne来请求所有权的互斥体的方法。 调用线程受到阻止,直到发生下列情况之一:
- 互斥体是发出信号,以指示它不属于。 在此情况下,WaitOne方法将返回true,调用线程是互斥体的所有权,并访问受该互斥体的资源。 完成后访问资源,必须调用线程ReleaseMutex方法来释放 mutex 的所有权。 示例部分中的第一个示例说明了此模式。
- 对的调用中指定的超时间隔WaitOne具有方法millisecondsTimeout或timeout参数已过。 在此情况下,WaitOne方法将返回false,并调用线程不会进一步尝试获取 mutex 的所有权。 在这种情况下,您应构建你的代码,以便由 mutex 保护资源的访问权限被拒绝对调用线程。 线程永远不会获取 mutex 的所有权,因为它不能调用ReleaseMutex方法。 示例部分中的第二个示例说明了此模式。
Mutex类强制线程标识,因此只能由获得它的线程可以释放互斥体。 与此相反,Semaphore类不会强制线程标识。 此外可以跨应用程序域边界传递了互斥体。
拥有 mutex 的线程可以请求中重复调用相同的互斥体WaitOne而不会阻止其执行。 但是,调用线程必须ReleaseMutex方法相同数量的次数与释放 mutex 的所有权。
因为Mutex类继承自WaitHandle,你还可以调用静态WaitHandle.WaitAll和WaitHandle.WaitAny方法来同步受保护资源的访问权限。
如果线程终止时拥有互斥体,则认为该 mutex 已放弃。 互斥体的状态设置为终止状态,并且下一个等待线程获取所有权。 从.NET Framework 的版本 2.0AbandonedMutexException获取已放弃的互斥体的下一个线程中引发。 在.NET Framework 2.0 版中之前, 未不引发任何异常。
注意:
放弃的 mutex 通常表明代码中的存在严重错误。 线程退出时不释放互斥体,由 mutex 保护的数据结构不可能处于一致状态。 为请求 mutex 所有权的下一个线程可以处理此异常并继续操作,如果可以验证数据结构的完整性。
对于系统范围的 mutex,放弃的 mutex 可能指示应用程序已突然终止(例如,通过使用 Windows 任务管理器终止)。
Mutex 有两种类型: 本地 mutex,未命名,并命名系统 mutex。 本地 mutex 仅存在于进程中。 可由您具有对引用的过程中的任何线程Mutex表示该互斥体的对象。 每个未命名Mutex对象表示单独的本地 mutex。
已命名的系统互斥体,显示整个操作系统,可用于同步进程的活动。 您可以创建Mutex使用接受名称的构造函数表示命名的系统互斥体对象。 可以在同一时间创建操作系统对象也可以在创建之前存在Mutex对象。 可以创建多个表示同一命名系统 mutex 的 Mutex 对象,还能使用 OpenExisting 方法打开现有的命名系统 mutex。
备注:
在运行终端服务的服务器,命名的系统互斥体可以包含两个级别的可见性。 如果其名称以前缀开头"Global",互斥体是在所有终端服务器会话中可见。 如果其名称以前缀开头"本地",互斥体是仅在终端服务器会话中可见的创建位置。 在这种情况下,具有相同名称单独的互斥锁可以存在于每个服务器上的其他终端服务器会话。 如果您不指定前缀创建已命名的互斥体时,它将前缀"本地"。 终端服务器会话中,只能由其前缀的名称不同的两个 mutex 有单独的互斥体,且都在终端服务器会话中对所有进程可见。 也就是说,前缀名称"Global"和"本地"描述相对于终端服务器会话、 不相对于进程的互斥体名称的作用域。
反斜杠 () 是互斥体名称中的保留字符。 请勿在互斥体名称中使用反斜杠 (),除非在终端服务器会话中使用互斥体的注释中另有规定。 否则,即使互斥体的名称表示现有文件,也可能引发 DirectoryNotFoundException。