C#多線程編程之lock語句應注意的問題

來源:互聯網
上載者:User

轉自:http://hi.baidu.com/oohacker/blog/item/6004e6fb712feb254e4aea24.html

 

 

有關C#鎖語句的討論網上說得很多, 但絕大多數只說了了怎麼用, 對於lock的注意事項卻很少, 尤其是關於lock(this), lock(typeof(ClassName))以及lock("thisLock")的討論更是鳳毛麟角, 就連MSDN中對於前面提及的三種lock可能導致的問題也是一筆帶過(http://msdn.microsoft.com/zh-cn/worldwide/c5kehkcz.aspx)。百度和Google了許久,終於收集到相關資料,今天把它寫下來,與大家一起分享。

    MSDN上指出,使用lock時不注意可能導致以下問題:

    1). 如果執行個體可以被公用訪問,將出現 lock (this) 問題。

    2). 如果 MyType 可以被公用訪問,將出現 lock (typeof (MyType)) 問題。

    3). 由於進程中使用同一字串的任何其他代碼都將共用同一個鎖,所以出現 lock(“myLock”) 問題。

    下面來討論這些問題:

     首先看lock(this)問題,如果一個類是公有的時,應該避免在基方法或屬性中使用lock(this)語句,因為如果有其他人使用你的組件,它並不瞭解你的組件內部是否使用了鎖,如果使用了而使用者又在類外部對類執行個體嘗試加鎖,則可能導致一個死結,下面是我從Google(原地址:http://www.toolazy.me.uk/template.php?content=lock(this)_causes_deadlocks.xml,原程式中由於將主線程睡眠,實際作業系統會自動切換可用線程,因而未能體現出死結的效果)找到的修改後的例子:

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace Test
{
class InternalClass
{
    public void TryLockThis()
    {
      Thread t = new Thread(ThreadFunction);
      t.Start();
    }

    private void ThreadFunction()
    {
      Thread.Sleep(3000); // 延遲,等待外部對對象執行個體加鎖
      Console.WriteLine("嘗試通過lock(this)加鎖下面的代碼塊...");

      while (true)
      {
        lock (this)
        {
          Console.WriteLine("執行內部鎖,1秒後繼續...");
          Thread.Sleep(1000);
          Console.WriteLine("內部鎖完成...");
        }
      }
    }
}

class ClassMain
{
    private InternalClass theClass = new InternalClass();

    public ClassMain()
    {
      theClass.TryLockThis();
      Console.WriteLine("在執行內部鎖之前對對象進行加鎖...");
      lock (theClass) // 如果注釋掉這句,ThreadFunction()中的lock將執行成功
      {
        Console.WriteLine("對象被鎖定, 在這裡我們獲得了一個死結...");

        while (true) { }
      }
    }

    [STAThread]
    static void Main(string[] args)
    {
      ClassMain cm = new ClassMain();

      Console.WriteLine("Press Enter to exit");
      Console.ReadLine();
    }
}
}

可以看到上述的程式會導致一個死結,程式的執行結果是這樣的:

在執行內部鎖之前對對象進行加鎖...
對象被鎖定, 在這裡我們獲得了一個死結...
嘗試通過lock(this)加鎖下面的代碼塊...

因此,應盡量避免甚至拒絕使用lock(this)這樣的語句。

同樣的,lock(typeof(ClassName))也有類似的問題,由於typeof語句返回的是一個類的類型執行個體,對於一個類來說只有一 個,如果在類的方法或執行個體方法中使用了typeof(className)這樣的語句,而在類的外部又嘗試對類進行加鎖,同樣可能導致死結。

關於lock("thisLock")的問題,是由.Net的字串處理模式導致的。
[MSDN]
公用語言運行庫通過維護一個表來存放字串,該表稱為拘留池,它包含程式中以編程方式聲明或建立的每個唯一的字串的一個引用。因此,具有特定值的字串的執行個體在系統中只有一個。
例如,如果將同一字串分配給幾個變數,運行庫就會從拘留池中檢索對該字串的相同引用,並將它分配給各個變數。

由於這個原因,假如你lock的字串對象在拘留池中(通常是編譯時間顯式聲明的用引號引起來的字元 串),那麼,這個對象也有可能被分配到另外一個變數,如果這個變數也使用了lock,那麼,兩個lock語句表面上lock的不同對象,但實質上卻是同一 個對象,這將造成不必要的阻塞,甚至可能造成死結。且不亦發現問題。

從下面的程式中可以看出一個問題:
      string a = "String Example";
      string b = "String Example";
      string c = (new StringBuilder()).Append("String Example").ToString();
      Console.WriteLine("a==b? {0}", object.ReferenceEquals(a, b));
      Console.WriteLine("a==c? {0}", object.ReferenceEquals(a, c));

上面程式執行的結果是:
    a==b? True
    a==c? False

從上面可以看出,a和b指向的是同一個引用,而lock正是通過引用來區分並加鎖臨界程式碼片段的。也就是說,如果在我們一程式的一個部分中使用了 lock("thisLock")進行加鎖,而在程式的另一個位置同樣也使用lock("thisLock")進行加鎖,則極有可能導致一個死結,因而是 很危險的。實際上,不只是lock("thisLock")這樣的語句,在lock中使用string類型的引用都有可能導致死結。

   上述就是C#中應避免使用lock(this), lock(typeof(className)), lock("thisLock")的原因。

 

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.