2017-10-31 2 views
2

私はConcurrentDictionaryを持っています。ConcurrentDictionaryはint型のハッシュセットをキーとしてlong型の値を持ちます。キーが辞書にない場合は、最初の要素で新しいハッシュセットを追加します。キーが存在する場合は、新しい要素を既存の辞書に追加します。 .Add()メソッドは、BOOLを返し、AddOrUpdateはHashSetのを期待しているため、ConcurrentDictionaryの値である新しい要素をハッシュセットに追加するには?

ConcurrentDictionary<long, HashSet<int>> myDic = new ConcurrentDictionary<long, HashSet<int>>(); 
int myElement = 1; 
myDic.AddOrUpdate(1, new Hashset<int>(){myFirstElement}, 
(key, actualValue) => actualValue.Add(myElement)); 

このコードの問題は三番目のパラメータである:

は、私はそのような何かをしようとしています。第1および第2のパラメータは正しい。

私の質問は、スレッドセーフな方法でハッシュセットに新しい要素を追加して重複を避けることができることです(これがハッシュセットを値として使用している理由です)。ハッシュセットの問題はスレッドセーフではないことです。最初に取得して後で新しい要素を追加すると、辞書外でやっていて問題が発生する可能性があります。

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

答えて

3

コンパイラエラーを修正するために、あなたはこれを行うことができます:

myDic.AddOrUpdate(1, new HashSet<int>() { myFirstElement }, 
    (key, actualValue) => { 
     actualValue.Add(myFirstElement); 
     return actualValue; 
    }); 

しかしこれは、あなたが潜在的に複数のスレッドからではなく、スレッドセーフHashSetに追加しているので、「更新」機能は、任意のロック内で実行されていないため、スレッドセーフではありません。これは値を失うなどの結果になる可能性があります(つまり、HashSetに1000個のアイテムを追加しましたが、最後には970個のアイテムしかありません)。 AddOrUpdateの更新機能には副作用があってはいけません。

あなたはHashSetに値を追加する上で自分自身をロックすることができます

myDic.AddOrUpdate(1, new HashSet<int>() { myFirstElement }, 
    (key, actualValue) => { 
     lock (actualValue) { 
      actualValue.Add(myFirstElement); 
      return actualValue; 
     } 
    }); 

をしかし、あなたが最初の場所でロックフリー構造(ConcurrentDictionary)を使用している理由は、その後の質問です。それ以外にも、他のコードでは辞書からHashSetが得られ、そこにロックをかけずに値を追加すると、すべてが役に立たなくなる可能性があります。したがって、何らかの理由でその方法に進むことに決めた場合は、その辞書からHashSetにアクセスするときに、すべてのコードがロックされていることを確認する必要があります。

代わりに、HashSetの代わりに並行収集を使用してください。私が知る限りConcurrentHashSetはありませんが、別のConcurrentDictionaryをダミーキーに置き換えて使用することもできます(カスタム実装のためにインターネットを見て回ることもできます)。

サイドノート。キーが既に存在するため、その辞書が必要とされていない場合でも、AddOrUpdateを呼び出すときにここで

myDic.AddOrUpdate(1, new Hashset<int>(){myFirstElement}, 

新しいHashSetを毎回作成します。代わりに、追加の値工場でオーバーロードを使用します。

myDic.AddOrUpdate(1, (key) => new HashSet<int>() { myFirstElement }, 

編集:ハッシュセットとしてConcurrentDictionaryの使用例:

var myDic = new ConcurrentDictionary<long, ConcurrentDictionary<int, byte>>(); 
long key = 1; 
int element = 1; 
var hashSet = myDic.AddOrUpdate(key, 
    _ => new ConcurrentDictionary<int, byte>(new[] {new KeyValuePair<int, byte>(element, 0)}), 
    (_, oldValue) => { 
     oldValue.TryAdd(element, 0); 
     return oldValue; 
    }); 
+0

あなたのヒントをありがとうございました。ダミーキーで値としてCouncurrentDictionaryを使用する方法の例を教えてください。ありがとう。 –

+0

@ÁlvaroGarcía更新の回答 – Evk

+0

の例でこの例では。私がハッシュセットを取得してから新しい要素を追加しようとすると、誰かがそれを削除する可能性があるので、追加しようとするとエラーが発生する可能性があります。または、少なくともハッシュに要素がないことを辞書に追加します。それが正しいか? –

1

あなたは、中括弧で無名関数の定義をラップする場合は、関数の本体に複数のステートメントを定義するため、次のように戻り値を指定することができます。

myDic.AddOrUpdate(1, new HashSet<int>() { myFirstElement }, 
(key, actualValue) => { 
    actualValue.Add(myElement); 
    return actualValue; 
}); 
関連する問題