C#中的lock關鍵字有何做用

  做爲C#的程序員來講,在遇到線程同步的需求時最經常使用的就是lock關鍵字。但如何正確並有效地使用lock,倒是可否高效地達到同步要求的關鍵。正由於如此,程序員須要徹底理解lock究竟爲程序作了什麼。程序員

  所涉及的知識點安全

• lock的等效代碼多線程

• System.Threading.Monitor類型的做用和使用方法測試

  分析問題線程

1.lock的等效代碼設計

  在.NET的多線程程序中,常常會遇到lock關鍵字來控制同步,好比下列代碼:對象

private object o = new object();索引

public void Work()rem

{同步

  lock(o)

  {

    //作一些須要線程同步的工做

  }

}

  事實上,lock這個關鍵字是C#爲方便程序員而定義的語法,它等效於安全地使用System.Threading.Monitor類型。上面的代碼就直接等效於下面的代碼:

private object o = new object();

public void Work()

{

  //這裏很重要,是爲了不直接使用私有成員o,而致使線程不安全

  object temp = o;

  System.Threading.Monitor.Enter(temp);

  try

  {

    //作一些須要線程同步的工做

  }

  finally

  {

    System.Threading.Monitor.Exit(temp);

  }

}

  正如你看到的,真正實現了線程同步功能的,就是System.Threading.Monitor類型,lock關鍵字只是用來代替調用Enter、Exit方法,而且將全部的工做包含在try塊內,以保證其最終退出同步。

2.System.Threading.Monitor類型的做用和使用

  在前文中已經提到了,Monitor類型的Enter和Exit方法用來實現進入和退出對象的同步。具體來講,當Enter方法被調用時,對象的同步索引將被檢查,而且.NET將負責一系列的後續工做,來保證對象訪問時線程的同步,而Exit方法的調用,則保證了當前線程釋放該對象的同步塊。

  示例

  下列演示了自定義的類如何利用lock關鍵字(也就是Monitor類型)來實現線程同步,具體定義了一個包含須要同步執行方法的類型。

/// <summary>

/// 演示同步鎖

/// </summary>

public class MyLock

{

    //用來在靜態方法中同步

    private static object o1 = new object();

    //用來在成員方法中不一樣

    private object o2 = new object();

    //成員變量

    private static int i1 = 0;

    private int i2 = 0;

    /// <summary>

    /// 測試靜態方法的同步

    /// </summary>

    /// <param name=" handleObject ">同步時操做的對象</param>

    public static void Increment1(object handleObject)

    {

        lock (o1)

        {

            Console.WriteLine("i1的值爲:{0}", i1);

            //這裏刻意製造線程並行機會,來檢查同步的功能

            Thread.Sleep(200);

            i1++;

            Console.WriteLine("i1自增後爲:{0}", i1);

        }

    }

    /// <summary>

    /// 測試成員方法的同步

    /// </summary>

    /// <param name=" handleObject ">同步時操做的對象</param>

    public void Increment2(object handleObject)

    {

        lock (o2)

        {

            Console.WriteLine("i2的值爲:{0}", i2);

            //這裏刻意製造線程並行機會,來檢查同步的功能

            Thread.Sleep(200);

            i2++;

            Console.WriteLine("i2自增後爲:{0}", i2);

        }

    }

}

 

這樣在main方法中,調用該類型對象的方法和其靜態方法,測試其同步的效果。代碼以下

/// <summary>

/// 程序入口

/// </summary>

class Program

{

  /// <summary>

   /// 測試同步效果

   /// </summary>

  static void Main(string[] args)

  {

         //開始多線程

         Console.WriteLine("開始測試靜態方法的同步");

         for (int i = 0; i < 5; i++)

         {

             Task t = new Task(MyLock.Increment1, i);

             t.Start();

         }

         //這裏等待線程執行結束

        Thread.Sleep(3 * 1000);

         Console.WriteLine("開始測試成員方法的同步");

         MyLock myLock = new MyLock();

         //開始多線程

         for (int i = 0; i < 5; i++)

        {

             Thread t = new Thread(myLock.Increment2);

             t.Start();

         }

         Console.Read();

  }

}

下面是程序的執行結果:

開始測試靜態方法的同步

i1的值爲:0

i1自增後爲:1

i1的值爲:1

i1自增後爲:2

i1的值爲:2

i1自增後爲:3

i1的值爲:3

i1自增後爲:4

i1的值爲:4

i1自增後爲:5

開始測試成員方法的同步

i2的值爲:0

i2自增後爲:1

i2的值爲:1

i2自增後爲:2

i2的值爲:2

i2自增後爲:3

i2的值爲:3

i2自增後爲:4

i2的值爲:4

i2自增後爲:5

 

   總結

  能夠看到,線程同步被很好地保證了。這裏須要強調的是,線程同步自己違反了多線程並行運行的原則,因此讀者在使用線程同步時應該儘可能作到把lock加在最小的程序塊上。若是一個方法有大量的代碼須要線程同步,那就須要從新考慮程序的設計了,是否真的有必要進行多線程處理,畢竟線程自己的開銷也是至關大的。

  對靜態方法的同步,通常採用靜態私有的引用成員,而對成員方法的同步,通常採用私有的引用成員。讀者須要注意靜態和非靜態成員的使用,都把同步對象申明爲私有,這是保證線程同步高效而且正確的關鍵點。

 

說明:以上內容是根據網上內容進行整理。

相關文章
相關標籤/搜索