1、從“最簡單”的單例模式說起:
public class Singleton
{
private static Singleton instance = null;
private static readonly object syncRoot = new object();
private Singleton()
{
}
public static Singleton GetInstance()
{
if (instance == null)
{
lock (syncRoot)
{
if (instance == null)
{
instance = new Singleton();
}
}
}
return instance;
}
}
稍微瞭解設計模式基礎知識的人對上面的代碼應該不會陌生。上次去面試,主考官就讓樓豬寫個單例。雖然平時已經很久沒有刻意去使用設計模式編程了,但是對於這個單例模式,咩哈哈,雖然大丈夫喜怒不形於色,但是nc樓豬當時那個意氣風發躊躇滿志運筆如飛鋒芒畢露啊,嘿嘿,中規中矩地交了如上答案。面試官看了一下肯定了兩句,話鋒一轉,問lock那裡的object執行個體可以直接用instance替代嗎?new一個object是否多餘,可否寫成下面的形式:
public class Singleton
{
private static Singleton instance = null;
//private static readonly object syncRoot = new object();
private Singleton()
{
}
public static Singleton GetInstance()
{
if (instance == null)
{
lock (instance)//instance能替換syncRoot嗎?
{
if (instance == null)
{
instance = new Singleton();
}
}
}
return instance;
}
}
雖然在這個問題前面,面試官問的一個資料庫設計和幾個c#的小問題(有一個題樓豬當時竟然嘴硬說那是“奇技淫巧”,好怕怕)很見知識深度和修養,樓豬已經小有提防,但是這個問題還是再次讓樓豬自亂陣腳。以前從來沒人問過樓豬這個問題,樓豬也沒主動想過它,腦袋裡一片白茫茫,剛想要鎮定下來,面試官又把相同問題問了一遍。不及細想,雖然不能確定,但是還是回答“應該可以...吧”(語氣助詞用得妙啊)。
肯定是對這個回答和回答的語氣不滿意,面試官又提醒問lock機制是什麼樣的,說說c#大概是怎麼實現lock的。樓豬語無倫次說lock本質上和作業系統相關,lock就是當前線程將資源獨佔其他線程不能訪問該鎖定資源云云,背書都不會背了嗎?無語了。面試官說回去自己試試吧。聽他的語氣,答案當然不言自明了,小心靈遭受重大挫折和打擊啊。
回來後,驗證了一下面試官的問題,程式運行到lock的時候,拋出ArgumentNullException異常,“值不能為null”。為什麼lock一個指向null的引用執行個體不可以呢?
查了一下中文msdn:
lock 確保當一個線程位於代碼的臨界區時,另一個線程不進入臨界區。如果其他線程試圖進入鎖定的代碼,則它將一直等待(即被阻止),直到該對象被釋放。
老實說,直到看到最後一句“對象被釋放”才給了樓豬提示。某一對象可以指向null引用,但是null關鍵字是不需要釋放的吧?!是嗎?查了一下高人anytao的部落格,發現這篇認識全面的null給出了明確的答案。
接著樓豬又查看了一下經常可以替代lock的Monitor類,赫然發現它的靜態方法Enter(object obj)有一個異常類型ArgumentNullException,早知道,也不會那麼狼狽啊。
2、lock的陷阱
除了msdn裡提到的lock需要注意的兩點外,園子裡早就有高手總結過了,可以參考這一篇。
3、最後自己總結得出的三個結論:
a、c#中null是不能lock的(null會分配記憶體嗎,會佔用系統資源嗎?和樓豬一起思考和探討吧)。
b、高手一出手,往往可以化腐朽為神奇。
c、對自己說:
加油。