2016-05-04 9 views
1

私はC#で簡単なキャッシュロジックをテストしています。以下はマルチスレッドでローカル変数をロックする必要がありますか?

public class CacheManager 
{ 
    static List<string> _list = new List<string>(); 
    static readonly object _syncobject = new object(); 

    public static void ReloadList() 
    { 
     List<string> list = new List<string>(); 

     Random r = new Random(); 
     var count = r.Next(10); 

     for (int i = 0; i < count; i++) 
     { 
      list.Add("list" + i); 
     } 

     //lock (_syncobject) 
     { 
      _list = list; 
     } 

    } 


    public static IEnumerable<string> GetList() 
    { 
     //lock (_syncobject) 
     { 
      return _list; 
     } 
    } 

} 

のCacheManagerを消費する多くのスレッドを生成クラスがされています:ここに私のCacheManagerクラスがある

class Program 
{ 
    static void Main(string[] args) 
    { 

     //threads for re-loading the list 
     for (int i = 0; i < 3; i++) 
     { 
      Thread reloadThread = new Thread(ReloadList); 
      reloadThread.Start(); 
     } 

     //threads for getting the list 
     for (int i = 0; i < 10; i++) 
     { 
      Thread tget = new Thread(PrintList); 
      tget.Start(); 
     } 

     //threads for getting the list and storing in local variable then use it 
     for (int i = 0; i < 10; i++) 
     { 
      Thread tget = new Thread(PrintListWithLocalVariable); 
      tget.Start(); 
     } 

     Console.ReadKey(); 

    } 

    private static void ReloadList() 
    { 
     do 
     { 
      Console.WriteLine("Reloading **********"); 
      CacheManager.ReloadList(); 

     } while (true); 
    } 

    private static void PrintList() 
    { 
     do 
     { 
      foreach (var item in CacheManager.GetList()) 
      { 
       if (item == null) 
        throw new Exception("i == null"); 

       Console.WriteLine(item); 
      } 

     } while (true); 
    } 

    private static void PrintListWithLocalVariable() 
    { 
     do 
     { 
      var list = CacheManager.GetList(); 
      foreach (var listitem in list) 
      { 
       var i = list.FirstOrDefault(x => x.Equals(listitem)); 
       if (i == null) 
        throw new Exception("i == null"); 
       Console.WriteLine("Printing with Local variable:" + listitem); 
      } 

     } while (true); 
    } 


} 

私の理解で私たちはCacheManagerの中_list変数をロックする必要がありますが、私たちのようには見えませんでしたそれが必要です。上記のテストを1時間ほど実行しましたが、何のエラーもありませんでした。 ReloadThreadはリスト項目、リストをループしている他のスレッドの乱数をリロードしていますが、私は問題があると思っていました。誰も私のプログラムが問題なく実行されている理由を説明することはできますか?

ありがとうございました。

+0

独自のキャッシュを作成する必要はありますか? https://msdn.microsoft.com/en-us/library/system.runtime.caching.memorycache(v=vs.110).aspx。また、ConcurrentCollectionを使用してみてください。 https://msdn.microsoft.com/en-us/library/dd997305(v=vs.110).aspx –

+0

FYI、 '_list'は「ローカル」変数ではありません。 – crashmstr

+2

説明は:あなたが幸運を得たからです。あなたはこれを次の100回実行すると幸運かもしれません。しかし、それはあなたが正しく働くために必要とするとすぐに不快な場所であなたを回して刺すでしょう。 – nvoigt

答えて

2

_listは、すべてのインスタンスが_listの同じインスタンスを共有することを意味する静的変数です。つまり、CacheManagerのすべてのインスタンスが同じインスタンスを共有します。同時実行の問題を防ぐために、_listへのアクセスを実際にロックする必要があります。コメントに記載されているように、List(of T)はスレッドセーフではないため、List(of T)の代わりにConcurrentCollectionを使用する必要があります。

+0

実際、私は私のプロダクションロジックにロックを追加しましたが、それ以前にはテストしていましたが、ロックなしでロジックがうまく動作していて、 ConcurrentCollectionは、私がリストに項目を追加したり取得したりしていないのでここでは役に立ちません。 ReloadListロジックでは、私は関数変数でクラス変数を設定しています。私はIEnumerableを返しています。消費者のためのコピーを作成すると思います。 – Santosh

+0

戻り値の型をIEnumerable(T)に設定しても、 '_list'のコピーは作成されません。単にそれを派生しない型として返します。 '_list'のコピーを返すことを望むなら、ゲッターコードを' return _list.ToArray(); 'に変更して、リストをコピーします(オブジェクトはコピーしません)。より適切なアクションは、 'return _list.ToList()。AsReadOnly();'(ToList()は元のリストもコピーします)を使用して読み取り専用として返すことです。 – DVK

関連する問題