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


  作為C#的程序員來說,在遇到線程同步的需求時最常用的就是lock關鍵字。但如何正確並有效地使用lock,卻是能否高效地達到同步要求的關鍵。正因為如此,程序員需要完全理解lock究竟為程序做了什么。

  所涉及的知識點

• lock的等效代碼

• System.Threading.Monitor類型的作用和使用方法

  分析問題

1.lock的等效代碼

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

private object o = new object();

public void Work()

{

  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加在最小的程序塊上。如果一個方法有大量的代碼需要線程同步,那就需要重新考慮程序的設計了,是否真的有必要進行多線程處理,畢竟線程本身的開銷也是相當大的。

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

 

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM