天天看點

深入淺出多線程系列之五:一些同步構造(上篇)

1:Mutex

Mutex 就像一個C# lock一樣,不同的是它可以跨程序.

進入和釋放一個Mutex要花費幾毫秒,大約比C#的lock慢50倍。

使用一個Mutex的執行個體,調用WaitOne方法來擷取鎖,ReleaseMutex方法來釋放鎖。

因為Mutex是跨程序的,是以我們可以使用Mutex來檢測程式是否已經運作。

<a></a>

        public static void MainThread()

        {

            using (var mutex = new Mutex(false, "LoveJenny OneAtATimeDemo"))

            {

                if (!mutex.WaitOne(TimeSpan.FromSeconds(3), false))

                {

                    Console.WriteLine("隻能運作一個應用程式!");

                    return;

                }

                RunProgram();

            }

        }

2:Semaphore:

一個Semaphore就像一個酒吧一樣,通過門衛來限制它的客人,一旦到達限制,沒有人可以進入,

人們會在門外乖乖的排隊,一旦有一個人離開酒吧,排隊中的人就可以進入了一個了。

下面是個例子:

    class TheClub

    {

       //隻能容納三個人的酒吧

        static SemaphoreSlim _sem = new SemaphoreSlim(3);

        public static void MainThread()

            for (int i = 1; i &lt;= 5; i++)

                new Thread(Enter).Start(i); //有5個人向進入

        static void Enter(object id)

            Console.WriteLine(id + " 想要進入了");

            _sem.Wait();

            Console.WriteLine(id+" 已經進入了!");

            Thread.Sleep(1000 * (int)id);

            Console.WriteLine(id + " 離開了?");

            _sem.Release();

    }

3:AutoResetEvent

一個AutoResetEvent就像十字轉門一樣,插入一張票就讓一個人通過,”Auto”代表門會自動的關上。

在十字門外面的人可以調用WaitOne方法來阻塞,等待。一旦有人插入了票(調用Set方法),就可以讓外面等待的人(調用WaitOne方法的線程)通過了。

建立AutoResetEvent有一個參數。

static EventWaitHandle _waitHandle = new AutoResetEvent(false);

其中false在msdn的解釋是:初始狀态為非終止,

按照我個人的了解false代表了十字轉門非終止,是以可以正常的進入,等待。

而如果是true的話:初始狀态為終止,也就是代表已經調用了Set了,

就是說十字轉門已經停止了,是以接下來如果有人調用了WaitOne方法,這個調用WaitOne方法的人直接就可以進入了,不需要再插入票(不需要調用Set)了,之後的調用和false一緻,這一點可以認為AutoResetEvent具有記憶功能,它記住了上次門是打開的狀态。是以調用waitone方法可以進入。

class ThreadAutoResetEvent

        static EventWaitHandle _waitHandle = new AutoResetEvent(false);

            new Thread(Waiter).Start();

            Thread.Sleep(2000);

            _waitHandle.Set();

        static void Waiter()

            Console.WriteLine("Waiting...");

            _waitHandle.WaitOne();

            Console.WriteLine("Notified");

}

很簡單,Waiter執行到Waiting…後,就開始調用WaitOne了,是以在門外排隊等待。

而主線程在睡了兩秒後,開始插入一張票(Set).是以Waiter就繼續執行,是以列印Notified

接下來我們使用AutoResetEvent來模拟實作生産消費問題:

class ProducerConsumerQueue:IDisposable

        EventWaitHandle _wh = new AutoResetEvent(false);

        Thread _worker;

        readonly object _locker = new object();

        Queue&lt;string&gt; _tasks = new Queue&lt;string&gt;();

        public ProducerConsumerQueue()

            //建立并啟動工作線程

            _worker = new Thread(Work);

            _worker.Start();

        public void EnqueueTask(string task)

            lock (_locker) _tasks.Enqueue(task);

            _wh.Set(); //一旦有任務了,喚醒等待的線程

        public void Dispose()

            EnqueueTask(null);

            _worker.Join(); //等待_worker線程執行結束

            _wh.Close();

        void Work()

            while (true)

                string task = null;

                lock (_locker)

                    if (_tasks.Count &gt; 0) 

                    {

                        task = _tasks.Dequeue();

                        if (task == null)

                            return;

                    }

                    if (task != null) //如果有任務的話,執行任務

                        Console.WriteLine("Performing task: " + task);

                        Thread.Sleep(1000);

                    else //否則阻塞,去睡覺吧

                        _wh.WaitOne();

主線程調用如下:

        public static void Main()

            using (ProducerConsumerQueue q = new ProducerConsumerQueue())

                q.EnqueueTask("Hello");

                for (int i = 0; i &lt; 10; i++) q.EnqueueTask("Say " + i);

                q.EnqueueTask("Goodbye!");

4:ManualResetEvent:

一個ManualResetEvent就是一個普通門,

調用Set方法門就打開了,允許任意數量的人進入。

調用WaitOne方法就開始等待進入。

調用Reset方法門就關閉了。

在一個關閉的門上調用WaitOne方法就會被阻塞。

當門下次被打開的時候,所有等待的線程都可以進入了。

除了這些不同外,一個ManualResetEvent和AutoResetEvent類似。

在Framework4.0中ManualResetEvent提供了一個優化版本。ManualResetEventSlim。後面的版本速度更快,并且支援取消(CancellationToken).

本文轉自LoveJenny部落格園部落格,原文連結:http://www.cnblogs.com/LoveJenny/archive/2011/05/24/2053677.html,如需轉載請自行聯系原作者