1. 程式人生 > >C#多執行緒學習(九)lock,Monitor,Mutex的區別

C#多執行緒學習(九)lock,Monitor,Mutex的區別

lock和Monitor的區別

一、lock的底層本身是Monitor來實現的,所以Monitor可以實現lock的所有功能。
二、Monitor有TryEnter的功能,可以防止出現死鎖的問題,lock沒有。

Mutex和其他兩者的區別

個人測試三個都是在限制執行緒之外的互斥,執行緒之內,都不限制,同一個執行緒如果被lock兩次。是不會出現死鎖的。所以Mutex本身可以實現lock和Monitor所有的操作。至少從功能上講是這樣的。
但是Mutex是核心級別的,消耗較大的資源,不適合頻繁的操作,會降低操作的效率。所以一般被呼叫部分的資源鎖,常常用lock或者Monitor,可以提高效率。而執行緒和執行緒間的協調,可以用Mutex,因為相互互斥切換的機會會大大的降低,效率就不再那麼的重要了。
Mutex本身是可以系統級別的,所以是可以跨越程序的。比如我們要實現一個軟體不能同時開啟兩次,那麼Mutex是可以實現的,而lock和monitor是無法實現的

在多執行緒中,為了使資料保持一致性必須要對資料或是訪問資料的函式加鎖,在資料庫中這是很常見的,但是在程式中由於大部分都是單執行緒的程式,所以沒有加鎖的必要,但是在多執行緒中,為了保持資料的同步,一定要加鎖,好在Framework中已經為我們提供了三個加鎖的機制,分別是Monitor類、Lock關鍵字和Mutex類。

        其中Lock關鍵詞用法比較簡單,Monitor類和Lock的用法差不多。這兩個都是鎖定資料或是鎖定被呼叫的函式。而Mutex則多用於鎖定多執行緒間的同步呼叫。簡單的說,Monitor和Lock多用於鎖定被呼叫端,而Mutex則多用鎖定呼叫端。
例如下面程式:由於這種程式都是毫秒級的,所以執行下面的程式可能在不同的機器上有不同的結果,在同一臺機器上不同時刻執行也有不同的結果,我的測試環境為vs2005, windowsXp , CPU3.0 , 1 G monery。
        程式中有兩個執行緒thread1、thread2和一個TestFunc函式,TestFunc會打印出呼叫它的執行緒名和呼叫的時間(mm級的),兩個執行緒分別以30mm和100mm來呼叫TestFunc這個函式。TestFunc執行的時間為50mm。程式如下:
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace MonitorLockMutex
{
    class Program
    {
        #region variable
        Thread thread1 = null;
        Thread thread2 = null;
        Mutex mutex = null;
        #endregion
        static void Main(string[] args)
        {
            Program p = new Program();
            p.RunThread();
            Console.ReadLine();
        }
        public Program()
        {
            mutex = new Mutex();
            thread1 = new Thread(new ThreadStart(thread1Func));
            thread2 = new Thread(new ThreadStart(thread2Func));
        }
        public void RunThread()
        {
            thread1.Start();
            thread2.Start();
        }
        private void thread1Func()
        {
            for (int count = 0; count < 10; count++)
            {
                TestFunc("Thread1 have run " + count.ToString() + " times");
                Thread.Sleep(30);
            }
        }
        private void thread2Func()
        {
            for (int count = 0; count < 10; count++)
            {
                TestFunc("Thread2 have run " + count.ToString() + " times");
                Thread.Sleep(100);
            }
        }
        private void TestFunc(string str)
        {
            Console.WriteLine("{0} {1}", str, System.DateTime.Now.Millisecond.ToString());
            Thread.Sleep(50);
        }
    }
}
執行結果如下:         可以看出如果不加鎖的話,這兩個執行緒基本上是按照各自的時間間隔+TestFunc的執行時間(50mm)對TestFunc函式進行讀取。因為執行緒在開始時需要分配記憶體,所以第0次的呼叫不準確,從第1~9次的呼叫可以看出,thread1的執行間隔約是80mm,thread2的執行間隔約是150mm。
現在將TestFunc修改如下:
private void TestFunc(string str)
{
   lock (this)
   {
      Console.WriteLine("{0} {1}", str, System.DateTime.Now.Millisecond.ToString());
      Thread.Sleep(50);
   }
}
或者是用Monitor也是一樣的,如下:
private void TestFunc(string str)
{
      Monitor.Enter(this);
      Console.WriteLine("{0} {1}", str, System.DateTime.Now.Millisecond.ToString());
      Thread.Sleep(50);
      Monitor.Exit(this);
}
其中Enter和Exit都是Monitor中的靜態方法。
執行Lock結果如下:         讓我們分析一下結果,同樣從第1次開始。相同執行緒間的呼叫時間間隔為執行緒執行時間+TestFunc呼叫時間,不同執行緒間的呼叫時間間隔為TestFunc呼叫時間。例如:連續兩次呼叫thread1之間的時間間隔約為30+50=80;連續兩次呼叫thread2之間的時間間隔約為100+50=150mm。呼叫thread1和thread2之間的時間間隔為50mm。因為TestFunc被lock住了,所以一個thread呼叫TestFunc後,當其它的執行緒也同時呼叫TestFunc時,後來的執行緒即進被排到等待佇列中等待,直到擁有訪問權的執行緒釋放這個資源為止。
        這就是鎖定被呼叫函式的特性,即只能保證每次被一個執行緒呼叫,執行緒優先順序高的呼叫的次數就多,低的就少,這就是所謂的強佔式。
        下面讓我們看看Mutex類的使用方法,以及與Monitor和Lock的區別。
將程式碼修改如下:
        private void thread1Func()
        {
            for (int count = 0; count < 10; count++)
            {
                mutex.WaitOne();
                TestFunc("Thread1 have run " + count.ToString() + " times");
                mutex.ReleaseMutex();
            }
        }

        private void thread2Func()
        {
            for (int count = 0; count < 10; count++)
            {
                mutex.WaitOne();
                TestFunc("Thread2 have run " + count.ToString() + " times");
                mutex.ReleaseMutex();
            }
        }

        private void TestFunc(string str)
        {
            Console.WriteLine("{0} {1}", str, System.DateTime.Now.Millisecond.ToString());
            Thread.Sleep(50);
        }
執行結果如下:
          可以看出,Mutex只能互斥執行緒間的呼叫,但是不能互斥本執行緒的重複呼叫,即thread1中waitOne()只對thread2中的waitOne()起到互斥的作用,但是thread1並不受本wainOne()的影響,可以呼叫多次,只是在呼叫結束後呼叫相同次數的ReleaseMutex()就可以了。
        那麼如何使執行緒按照呼叫順序來依次執行呢?其實把lock和Mutex結合起來使用就可以了,改程式碼如下:
        private void thread1Func()
        {
            for (int count = 0; count < 10; count++)
            {
                lock (this)
                {
                    mutex.WaitOne();
                    TestFunc("Thread1 have run " + count.ToString() + " times");
                    mutex.ReleaseMutex();
                }
            }
        }

        private void thread2Func()
        {
            for (int count = 0; count < 10; count++)
            {
                lock (this)
                {
                    mutex.WaitOne();
                    TestFunc("Thread2 have run " + count.ToString() + " times");
                    mutex.ReleaseMutex();
                }
            }
        } http://blog.csdn.net/xwdpepsi/article/details/8036312