C#多线程学习(六) 互斥对象

来源:互联网 发布:淘宝的红包口令在哪里 编辑:程序博客网 时间:2024/05/18 07:03

本文参考MSDN总结

 

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

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

 

     如果线程在拥有互斥体时终止,则称此互斥体被放弃。此互斥体被设置为终止状态,下一个等待的线程获得所属权。如果没有线程拥有互斥体,则互斥体状态为终止。从 .NET Framework 2.0 版开始,需要该互斥体的下一个线程将引发 AbandonedMutexException。在 .NET Framework 2.0 版之前,这样不会引发任何异常。

Caution note警告

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

 

      互斥体有两种类型:局部互斥体和已命名的系统互斥体。如果使用接受名称的构造函数创建 Mutex 对象,则该对象与具有该名称的操作系统对象关联。已命名的系统互斥体在整个操作系统中都可见,可用于同步进程活动。您可以创建多个 Mutex 对象来表示同一个已命名的系统互斥体,也可以使用 OpenExisting 方法打开现有的已命名系统互斥体。

局部互斥体仅存在于您的进程内。您的进程中任何引用局部 Mutex 对象的线程都可以使用它。每个 Mutex 对象都是一个单独的局部互斥体。

 

      我们可以把Mutex看作一个出租车,乘客看作线程。乘客首先等车,然后上车,最后下车。当一个乘客在车上时,其他乘客就只有等他下车以后才可以上车。而线程与Mutex对象的关系也正是如此,线程使用Mutex.WaitOne()方法等待Mutex对象被释放,如果它等待的Mutex对象被释放了,它就自动拥有这个对象,直到它调用Mutex.ReleaseMutex()方法释放这个对象,而在此期间,其他想要获取这个Mutex对象的线程都只有等待。

 

Mutex例子(参考MSDN代码修改)

using System;
using System.Threading;
using System.Collections;

namespace MutexExample
{
    public class MutexTest
    {
        // Create a new Mutex. The creating thread does not own the
        // Mutex.

        private Mutex mutex = new Mutex();
        private int threadNums = 3;

        private void ThreadProc()
        {
            this.UseResource();
        }

        private void UseResource()
        {
            // Wait until it is safe to enter.
            mutex.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.
            mutex.ReleaseMutex();
        }

        public static void Main(string[] args)
        {
            Console.WriteLine("Thread Operation Start./n");

            MutexTest mutexTest = new MutexTest();
            for (int i = 0; i < mutexTest.threadNums; i++)
            {
                Thread myThread = new Thread(new ThreadStart(mutexTest.ThreadProc));
                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.

 

            Console.ReadLine();
        }
    }
}

 

运行结果如下:

 

 

一个更加复杂更具代表性的例子:

using System;
using System.Threading;
using System.Collections;

namespace ThreadExample
{
    public class MutexSample
    {
        static Mutex gM1;
        static Mutex gM2;
        const int ITERS = 100;
        static AutoResetEvent Event1 = new AutoResetEvent(false);
        static AutoResetEvent Event2 = new AutoResetEvent(false);
        static AutoResetEvent Event3 = new AutoResetEvent(false);
        static AutoResetEvent Event4 = new AutoResetEvent(false);

        public static void Main(String[] args)
        {
            Console.WriteLine(" Mutex Sample /n");


            //创建一个Mutex对象,并且命名为MyMutex
            gM1 = new Mutex(true, "MyMutex");


            //创建一个未命名的Mutex 对象.
            gM2 = new Mutex(true);
            Console.WriteLine(" - Main Owns gM1 and gM2/n");

            AutoResetEvent[] evs = new AutoResetEvent[4];
            evs[0] = Event1; //为后面的线程t1,t2,t3,t4定义AutoResetEvent对象
            evs[1] = Event2;
            evs[2] = Event3;
            evs[3] = Event4;

            MutexSample tm = new MutexSample();
            Thread t1 = new Thread(new ThreadStart(tm.t1Start));
            Thread t2 = new Thread(new ThreadStart(tm.t2Start));
            Thread t3 = new Thread(new ThreadStart(tm.t3Start));
            Thread t4 = new Thread(new ThreadStart(tm.t4Start));


            t1.Start();// 使用Mutex.WaitAll()方法等待一个Mutex数组中的对象全部被释放
            t2.Start();// 使用Mutex.WaitOne()方法等待gM1的释放
            t3.Start();// 使用Mutex.WaitAny()方法等待一个Mutex数组中任意一个对象被释放
            t4.Start();// 使用Mutex.WaitOne()方法等待gM2的释放

 

            Thread.Sleep(2000);


            Console.WriteLine("/n - Main releases gM1/n");
            gM1.ReleaseMutex(); //线程t2,t3结束条件满足

 

            Thread.Sleep(1000);
            Console.WriteLine(" - Main releases gM2/n");
            gM2.ReleaseMutex(); //线程t1,t4结束条件满足

 

            //等待所有四个线程结束
            WaitHandle.WaitAll(evs);


            Console.WriteLine("/n Mutex Sample");
            Console.ReadLine();
        }

        public void t1Start()
        {
            Console.WriteLine("t1Start started, Mutex.WaitAll(Mutex[])");

            Mutex[] gMs = new Mutex[2];
            gMs[0] = gM1;//创建一个Mutex数组作为Mutex.WaitAll()方法的参数
            gMs[1] = gM2;
            Mutex.WaitAll(gMs);//等待gM1和gM2都被释放

 

            Thread.Sleep(2000);
            Console.WriteLine("t1Start finished, Mutex.WaitAll(Mutex[]) satisfied");
            gM1.ReleaseMutex();
            gM2.ReleaseMutex();

 

            Event1.Set(); //线程结束,将Event1设置为有信号状态
        }
        public void t2Start()
        {
            Console.WriteLine("t2Start started, gM1.WaitOne( )");
            gM1.WaitOne(); //等待gM1的释放

 

            Console.WriteLine("t2Start finished, gM1.WaitOne( ) satisfied");
            gM1.ReleaseMutex();

 

            Event2.Set(); //线程结束,将Event2设置为有信号状态
        }
        public void t3Start()
        {
            Console.WriteLine("t3Start started, Mutex.WaitAny(Mutex[])");

            Mutex[] gMs = new Mutex[2];
            gMs[0] = gM1; //创建一个Mutex数组作为Mutex.WaitAny()方法的参数
            gMs[1] = gM2;
            Mutex.WaitAny(gMs); //等待数组中任意一个Mutex对象被释放

 

            gM1.ReleaseMutex();

 

            Console.WriteLine("t3Start finished, Mutex.WaitAny(Mutex[])");

            Event3.Set(); //线程结束,将Event3设置为有信号状态
        }
        public void t4Start()
        {
            Console.WriteLine("t4Start started, gM2.WaitOne( )");
            gM2.WaitOne(); //等待gM2被释放

 

            Console.WriteLine("t4Start finished, gM2.WaitOne( )");
            gM2.ReleaseMutex();

 

            Event4.Set(); //线程结束,将Event4设置为有信号状态
        }
    }
}

 

运行结果:

 

原创粉丝点击