基元线程同步构造之 Mutes(互斥体)

核能气质少年 提交于 2020-01-30 07:12:52

  互斥体实现了“互相排斥”(mutual exclusion)同步的简单形式(所以名为互斥体(mutex))。

  互斥体禁止多个线程同时进入受保护的代码“临界区”(critical section)。

  因此,在任意时刻,只有一个线程被允许进入这样的代码保护区。任何线程在进入临界区之前,必须获取(acquire)与此区域相关联的互斥体的所有权。如果已有另一线程拥有了临界区的互斥体,其他线程就不能再进入其中。这些线程必须等待,直到当前的属主线程释放(release)该互斥体。什么时候需要使用互斥体呢?互斥体用于保护共享的易变代码,也就是,全局或静态数据。这样的数据必须通过互斥体进行保护,以防止它们在多个线程同时访问时损坏。

  互斥体(mutex)代表一个互斥的锁。它的工作方式和AutoResetEvent(或者计数为1的Semaphore)相似,因为所有这三种构造一次都只释放一个正在等待的线程,实例化一个mutex时,就默认释放了一个正在等待的线程,类似于初始化了一个 new AutoResetEvent(true)。下面展示了Mutex类的样子:

 

  public sealed class Mutex:WaitHandle{
       public Mutex();
       public void ReleaseMutex();
    }

 

  互斥体有一些而外的逻辑在里面,这造成它们比其他构造更复杂。首先,Mutex对象会查询调用线程的Int32ID,记录是那个线程获得了他。一个线程调用ReleaseMutex时,Mutex确保调用线程就是获取Mutex的那个线程。如若不然,Mutex对象的状态就不会改变,而ReleaseMutex会抛出一个System.ApplicationException.除此之外,如果拥有Mutex的一个线程因为任务原因而终止,那么在一个Mutex上等待的一个线程会因为抛出一个System.Threading.AbandonedMutexException异常而被唤醒。通常,这个异常会成为未处理的异常,从而终止整个线程。这是一件好事,因为一个线程在获取了一个Mutex之后,可能在它更新完Mutex所保护的数据之前终止。如果一个线程捕捉了AbandonedMutexException,就可能试图访问损坏的数据,造成无法预料的结果和安全问题。

   其次,Mutex对象维护着一个递归计数,它指明拥有该mutex的线程拥有了它多少次。如果一个线程当前拥有一个Mutex,然后该线程再次在Mutex上等待,递归计数将递增,这个线程允许继续运行。线程调用ReleaseMutex时,递归计数递减。只有在递归计数变成0时,另一个线程才能成为该mutex的所有者。

  大多数人都不喜欢这个额外的逻辑。这些“功能”是要付出代价的。Mutex对象需要更多的内存来容纳额外的线程ID和递归计数信息。另外,更重要的是,Mutex代码必须维护这些信息,这使锁变得更慢。如果一个应用程序需要(或希望)这些而外的功能,那么应用程序的代码可以自己来实现;代码不一定非要放到Mutext对象中。因此,许多人都会避免使用Mutex对象。

  当两个或者更多线程需要同时访问一个共享资源时,系统需要使用同步机制来确保一次只有一个线程使用该资源。Mutex是同步基元,它只向一个线程授予对共享资源的独占访问权。如果一个线程获取了互斥体,则要获取互斥体的第二个线程将被挂起,知道第一个线程释放该互斥体。

  可以使用WaitHandle.WaitOne方法请求互斥体的所属权。拥有互斥体的线程可以对WaitOne的重复调用中请求相同的互斥体而不会阻止其执行。但线程必须调用ReleaseMutex方法同样多的次数以释放互斥体的所属权。Mutex类强制线程标识,因此互斥体只能由获得它的线程释放。相反,Semaphore类不强制线程标识.

  如果线程在拥有互斥体是终止,则称此互斥体被放弃。将此mutex的状态设置为收到信号,下一个等待线程将获得所有权。从.net FrameWork 2.0版开始,在获取被放弃mutex的下一个线程中将引发AbandonedMutexException.在.net Framework 2.0版之前,这样不会引发任何异常.

   警告:如果出现被放弃的Mutex,通常表明代码中存在严重错误。如果某个线程在未释放互斥体时便退出,受此互斥体保护的数据结构可能处于不一致的状态。如果此数据结构的完整性能得到验证,下一个请求此互斥体所属权的线程就可以处理此异常并继续。

  对于系统范围的mutex,被放弃的mutex可能指示应用程序已突然终止(例如,通过使用windows任务管理器终止)。

  Mutex有两种类型:未命名的局部mutex和已命名的系统mutex.本地mutex仅存在于进程当中。您的进程中任何引用表示mutex的Mutex对象的下称都可以使用它。每个未命名的mutex对象都表示一个单独的局部mutex.

  已命名的系统互斥体在整个操作系统中都可见,可用于同步进程活动。您可以使用接受名称的构造函数创建表示已命名系统mutex的Mutex对象。同时也可以创建操作系统对象,或者它在创捷mutex对象之前就已存在。您可以创建多个Mutex对象来表示同一命名系统Mutex,而且您可以使用OpenExisting方法打开现有的命名系统mutex.

  说明:在运行终端服务的服务器上,已命名的系统 mutex 可以具有两级可见性。 如果名称以前缀“Global\”开头,则 mutex 在所有终端服务器会话中均为可见。 如果名称以前缀“Local\”开头,则 mutex 仅在创建它的终端服务器会话中可见。 在这种情况下,服务器上各个其他终端服务器会话中都可以拥有一个名称相同的独立 mutex。 如果创建已命名 mutex 时不指定前缀,则它将采用前缀“Local\”。 在终端服务器会话中,只是名称前缀不同的两个 mutex 是独立的 mutex,这两个 mutex 对于终端服务器会话中的所有进程均为可见。 即:前缀名称“Global\”和“Local\”说明 mutex 名称相对于终端服务器会话(而并非相对于进程)的范围。

  说明:应用到此类型或成员的 HostProtectionAttribute 特性具有以下 Resources 属性值:Synchronization | ExternalThreadingHostProtectionAttribute 不影响桌面应用程序(通常通过双击图标、键入命令或在浏览器中输入 URL 来启动这些应用程序)。有关更多信息,请参见 HostProtectionAttribute 类或 SQL Server 编程和宿主保护特性

  示例:此示例演示如何使用局部 Mutex 对象来同步对受保护资源的访问。

  

// This example shows how a Mutex is used to synchronize access
// to a protected resource. Unlike Monitor, Mutex can be used with
// WaitHandle.WaitAll and WaitAny, and can be passed across
// AppDomain boundaries.

using System;
using System.Threading;

class Test
{
    // Create a new Mutex. The creating thread does not own the
    // Mutex. 实例化一个Mutex时,默认就解除了一个等待的线程的阻塞.Mutex调用WaitOne时,就解除了阻塞,类似于实例化了:new AutoResetEvent(true).
    private static Mutex mut = new Mutex();
    private const int numIterations = 1;
    private const int numThreads = 3;

    static void Main()
    {
        // Create the threads that will use the protected resource.
        for(int i = 0; i < numThreads; i++)
        {
            Thread myThread = new Thread(new ThreadStart(MyThreadProc));
            myThread.Name = String.Format("Thread{0}", i + 1);
            myThread.Start();
        }

        // The main thread exits, but the application continues to
        // run until all foreground threads have exited.
    }

    private static void MyThreadProc()
    {
        for(int i = 0; i < numIterations; i++)
        {
            UseResource();
        }
    }

    // This method represents a resource that must be synchronized
    // so that only one thread at a time can enter.
    private static void UseResource()
    {
        // Wait until it is safe to enter.
        mut.WaitOne();

        Console.WriteLine("{0} has entered the protected area", 
            Thread.CurrentThread.Name);

        // Place code to access non-reentrant resources here.

        // Simulate some work.
        Thread.Sleep(500);

        Console.WriteLine("{0} is leaving the protected area\r\n", 
            Thread.CurrentThread.Name);

        // Release the Mutex.
        mut.ReleaseMutex();
    }
}

这是参考MSDN中的学习心得。。。。。。。。。。http://msdn.microsoft.com/zh-cn/library/System.Threading.Mutex(v=vs.110).aspx

    

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!