2017-11-01 7 views
2

1つのユニークな項目だけを同時に処理できるようにするには、同期メカニズムが必要です。そこで、Monitor.Enterを使用して、同じアイテムの他の同時実行をブロックしました。Monitor.Enterは他のタスクをブロックしません

ここでは、論理を検証するための私のコードとユニットテストのカットバージョンです。

私のコレクションのいくつかのアイテムは、Monitorからロックを取得できることがわかります。アイテムが取得されたときにロックを解除しないため、複数のアイテムが発生してはいけません。

なぜ私は通貨のコレクションのアイテムのいくつかが価値が2か3を持っているのを見ますか?私が持っている2時には3値として通貨コレクション内の項目のいくつかを見るのはなぜ

[TestClass] 
public class UnitTest2 
{ 
    public static ConcurrentDictionary<string, object> _keyLocks = 
     new ConcurrentDictionary<string, object>(); 

    public static object AcquireLock(string item) 
    { 
     object obj = _keyLocks.GetOrAdd(item, new object()); 
     Monitor.Enter(obj); 
    } 

    [TestMethod] 
    public void AcquireLock_MultipleRequest_OnlyAllow1Request() 
    { 
     Dictionary<string, int> currencies = new Dictionary<string, int>() { 
      { "USD",0 }, 
      { "EUR",0 }, 
      { "TRY",0 }, 
      { "AUD",0 }, 
      { "PLN",0 } 
     }; 

     int totalTask = 1000; 

     List<Task> tasks = new List<Task>(); 

     for (int i = 0; i < totalTask; i++) 
     { 
      string curr = currencies.Keys.ElementAt(i % currencies.Count); 
      tasks.Add(Task.Factory.StartNew((obj) => 
      { 
       string currStr = (string)obj; 
       AcquireLock(currStr); 
       currencies[currStr] += 1; 

       //Monitor.Exit will be implemented 
      }, curr)); 
     } 

     Thread.Sleep(10000); 

     foreach (var item in currencies.Keys) 
     { 
      Assert.AreEqual(1, currencies[item]); 
     } 
    } 
} 
+6

'ConcurrentDictionary'に追加するためのロックは必要ありません。' GetOrAdd() 'を使用してください。 – xxbbcc

+0

コメントをいただきありがとうございます。最初にGetOrAddを使用しましたが、thlsの後に問題が発生していると思われます。なぜなら、オーバーロードされたバージョンのValueOptをスレッドセーフではなく複数回呼び出すことができるからです。同じキーの新しいオブジェクト。 – Yucel

+1

バリューファクトリーの使い方を知っていればバリューファクトリーには何も問題はありません。特にあなたのケースでは、それはちょうど 'new object()'なので、複数回呼び出されても、実際には1つのオブジェクトだけがマップに配置されます。現在のコードよりもはるかに効率的です。 – xxbbcc

答えて

1

スレッドプールを使用してロックを取得しているためです。スレッドプールを使用するということは、同じスレッドで複数の操作を実行できることを意味します。もちろん、オブジェクトのモニタはスレッドごとに取得されます。私。特定のスレッドは同じロックを2回以上取得できます。

あなたの質問の下に掲載されたコメントを心に留めてください。同期メカニズムを混乱させ、冗長な方法で混在させています。

+0

なので、ThreadXで実行されている1つのタスクがXオブジェクトのロックを取得した後、タスクの終了後にThreadXがプールに解放され、別のタスクがThreadXで再度実行されてXオブジェクトに対するロックを取得できるようになり、 ThreadXはすでに古いタスクでロックを取得しているためです。私は正しい?はいの場合、私はどのようにロックを取得するために他のタスクをブロックするために達成するのだろうか? – Yucel

+0

_ "ThreadXは既に古いタスクでロックを取得しているためです。" - はい、そうです。 _ "もしそうなら、私はロックを得るために他の仕事をブロックするためにどのように達成するのですか?" - あなたの質問に重複して提出した(上のコメントの上の)リンクを読むと、[この回答](https://stackoverflow.com/a/4846160)には、スレッドアフィニティを持たない同期メカニズムを含むさまざまなオプションが記述されています。 –

関連する問題