C#使用Monitor類、Lock和Mutex類進行多線程同步

多線程程序是常常須要用到的,本文介紹C#使用Monitor類、Lock和Mutex類進行多線程同步。數據庫

在多線程中,爲了使數據保持一致性必需要對數據或是訪問數據的函數加鎖,在數據庫中這是很常見的,可是在程序中因爲大部分都是單線程的程序,因此沒有加鎖的必要,可是在多線程中,爲了保持數據的同步,必定要加鎖,好在Framework中已經爲咱們提供了三個加鎖的機制,分別是Monitor類、Lock關鍵字和Mutex類。         其中Lock關鍵詞用法比較簡單,Monitor類和Lock的用法差很少。這兩個都是鎖定數據或是鎖定被調用的函數。而Mutex則多用於鎖定多線程間的同步調用。簡單的說,Monitor和Lock多用於鎖定被調用端,而Mutex則多用鎖定調用端。 例以下面程序:因爲這種程序都是毫秒級的,因此運行下面的程序可能在不一樣的機器上有不一樣的結果,在同一臺機器上不一樣時刻運行也有不一樣的結果,個人測試環境爲vs2005, windowsXp , CPU3.0 , 1 G monery。         程序中有兩個線程thread一、thread2和一個TestFunc函數,TestFunc會打印出調用它的線程名和調用的時間(mm級的),兩個線程分別以30mm和100mm來調用TestFunc這個函數。TestFunc執行的時間爲50mm。程序以下:windows

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);
        }
    }
}
View Code

 

 運行結果以下:多線程

        能夠看出若是不加鎖的話,這兩個線程基本上是按照各自的時間間隔+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();
                }
            }
        }
相關文章
相關標籤/搜索