2012-02-16 76 views
3

我在我的應用程序,這是用來存放從我的數據庫中的數據和查找信息時,使用了大量的靜態列表的:C#使用()語句和數據的高速緩存

public static IList<string> Names; 

我也有一些方法來從數據庫中刷新這個數據:

public static void GetNames() 
{ 
    SQLEngine sql = new SQLEngine(ConnectionString); 
    lock (Names) 
    { 
     Names = sql.GetDataTable("SELECT * FROM Names").ToList<string>(); 
    } 
} 

我最初沒有必須到位鎖(),但我注意到很偶然,請求線程無法找到該列表中的信息。現在,我假設,如果請求線程試圖訪問名稱列表,它不能直到它完全更新。

這是lock()語句的正確方法和用法嗎?

作爲一個旁註,我注意到MSDN上的一個不應該在公共變量上使用lock()。有人能詳細說明我的特殊情況嗎?

+0

建議的做法是使用單獨的專用對象進行鎖定,而不是自己的位置 – 2012-02-16 07:30:15

+0

@HarisHasan:爲什麼? – jgauffin 2012-02-16 07:35:47

+0

http://stackoverflow.com/questions/230716/difference-between-locklocker-and-lockvariable-which-i-am-using – 2012-02-16 07:41:21

回答

3

lock僅僅是有用的,如果所有地方打算同步應用鎖。所以次你訪問Names你將被要求lock。目前,只能同時停止2個線程交換Names,坦率地說這不是一個問題,因爲參考交換無論如何都是原子。

另一個問題;據推測Names開始null?你不能lock a null。同樣,你不應該鎖定可能會改變參考。如果要同步,常用的方法是這樣的:

// do not use for your scenario - see below 
private static readonly object lockObj = new object(); 

然後lock(lockObj),而不是你的數據。

關於不鎖定外部可見的東西;是。那是因爲的其他一些代碼可以隨機選擇lock就可以了,這可能會導致意外的阻塞,並且很可能出現死鎖。

另一個很大的風險是,你的一些代碼獲得的名字,然後做一個sort/add/remove/clear/etc - 任何可以改變數據的東西。就我個人而言,我會在這裏使用只讀列表。事實上,只讀列表中,您擁有的只是一個參考交換;因爲這是原子,你不需要任何鎖定:

public static IList<string> Names { get; private set; } 
public static void UpdateNames() { 
    List<string> tmp = SomeSqlQuery(); 
    Names = tmp.AsReadOnly(); 
} 

最後:公共領域是非常非常很少是個好主意。因此,上述財產。這將由JIT內聯,所以這不是一種懲罰。

+0

+1謝謝。我可能應該顯示更多的代碼,名稱實際上是默認實例化的。嗯,你的一些意見實際上引導我到另一個問題.... – Simon 2012-02-16 07:48:35

+0

啊,和你的編輯然後回答一些他們! :) – Simon 2012-02-16 07:50:13

+0

+++ 1(如果我可以)不能相信我錯過了。 {get;私人設置; }正是我需要的。如果dat只在靜態方法中更新,我是否還需要鎖定()? – Simon 2012-02-16 08:03:44

3

不,這是不正確的,因爲任何人都可以直接使用Names屬性。

public class SomeClass 
{ 
    private List<string> _names; 
    private object _namesLock = new object(); 

    public IEnumerable<string> Names 
    { 
     get 
     { 
      if (_names == null) 
      { 
       lock (_namesLock) 
       { 
        if (_names == null) 
         _names = GetNames(); 
       } 
      } 

      return _names; 
     } 
    } 

    public void UpdateNames() 
    { 
     lock (_namesLock) 
      GetNames(); 
    } 

    private void GetNames() 
    { 
     SQLEngine sql = new SQLEngine(ConnectionString); 
     _names = sql.GetDataTable("SELECT * FROM Names").ToList<string>(); 
    } 
} 

儘量避免使用靜態方法。至少使用單身。

檢查,鎖定,檢查速度比鎖定快,因爲寫入只會發生一次。

在使用情況下分配屬性稱爲延遲加載。

由於無法鎖定爲null,因此需要使用_namesLock

+0

非常穩固 - 但假設他想更新名稱而不是空 - 只是想刷新數據? – Prescott 2012-02-16 07:39:04

+0

@Prescott:查看更新。 – jgauffin 2012-02-16 07:44:33

+0

正確使用時,靜態方法沒有任何問題。這種方法的一個問題是它可以轉換回可變列表 - 我已經看到人們會因此而導致混亂,主要是偶然地通過數據綁定enumerable(這也是一個列表)。數據綁定並不在意(它只知道'object'),並且愉快地改變它。 – 2012-02-16 07:46:06

0

從您已經顯示的oode中,第一次調用GetNames()的Names屬性爲null。我不知道空對象上的鎖會做什麼。我會添加一個變量來鎖定。

static object namesLock = new object(); 

然後在GetNames()

lock (namesLock) 
{ 
     if (Names == null) 
     Names = ...; 
} 

我們做測試,如果鎖()內停止競爭條件。我假設GetNames()的調用者也做了相同的測試。