2009-10-26 57 views
1

我的web應用程序需要這種類似於數據庫連接的對象。創建起來很慢,而且很少使用,所以我只想保留它的一個實例。如果多個請求同時需要它們,它們將lock()對象並序列化它們的訪問權限。如何創建一個IDisposable線程安全的單實例?

爲了讓事情更有趣,該對象是一個IDisposable對象,可以關閉。當然,我會避免編寫關閉它的代碼,但是......你知道......比對不起更安全,對吧?

於是,天真的辦法是實現它是這樣的:

private static DBClass _Instance; 
private static object _DBLock = new object(); 

public DBClass GetDB() 
{ 
    if (_Instance == null) 
     lock (_DBLock) 
      if (_Instance == null) 
       _Instance = CreateInstance(); 
    lock (_Instance) 
     if (_Instance.Disposed) 
      _Instance = CreateInstance(); 
    return _Instance; 
} 

然後它將被使用,如:

lock (var Conn = Global.GetDB()) 
{ 
    // Use Conn here. 
} 

這可能會工作的大部分時間,但我看到一個可以被利用的開放 - 如果兩個線程同時調用它,它們會同時獲得相同的實例,然後在另一個線程獲取lock()之前,它仍然可以使用Dispose()。因此 - 後來的代碼失敗。在每個使用它的地方檢查Disposed也似乎很尷尬。實際上,lock()看起來也很尷尬,但實例本身並不是線程安全的,所以我無能爲力。

你將如何實現這一點?

回答

2

首先,我建議避免重複檢查鎖定。很容易弄錯 - 就像你在這種情況下做的那樣,不要讓變量變得易變。

鑑於你不想實際上處置該對象,是否有任何理由不把它包裝在實現相同的API,但沒有暴露處置的東西?這樣你也可以封裝鎖定,具體取決於你實際使用對象的方式。這樣做的另一種方法是按照Action<DBClass>的方式公開一個API--你傳遞你想要的對象的行爲,並且幫助器負責創建實例並適當地鎖定。

最後一點:所有這些在可測試性方面都感覺有些棘手。我不知道你是否是單元測試的愛好者,但單身人士經常會讓測試的生活變得尷尬。

+0

問題是,這件事返回其他對象,它持有對它的引用並對其執行操作。除非我包裝幾百個課程,否則將無法使用。 Action <>方法值得注意,但我想知道代碼中增加的混亂是否值得。我想要一個優雅的方法,但這是有點去另一種方式... – 2009-10-26 11:22:00

+0

順便說一句 - 我在那裏使用一個鎖()。爲什麼需要易變? – 2009-10-26 11:25:20

+1

由於* secondary *鎖定,您可能確實沒關係 - 但考慮到您涉及到兩個不同的鎖,因此我完全不清楚它真的*是*沒問題。我個人使用一個單一的鎖 - 把整個方法放在一個使用'_DBLock'的鎖定語句中。內存模型的微妙之處非常棘手。同樣,如果另一個線程在不使用鎖定的情況下使用該對象,但不能保證你會發現它並創建一個新的實例。基本上感覺這個設計爲很多微妙的錯誤開闢了空間。 – 2009-10-26 11:31:43

相關問題