2016-03-23 33 views
3

消費者クラスは、シングルトンです。どんな方法でも条件付きでのロックリソースが可能ですか?現在、外部リソースは"グローバルに"がAsyncEx(https://github.com/StephenCleary/AsyncEx)フレームワークでロックされています。しかし、キーの入力メッセージに基づいてロックする方がいいので、異なるキーのメッセージでリソースをロックしないようにしてください。条件付きロックリソース

public class Consumer 
{ 
    private readonly AsyncLock _mutex = new AsyncLock(); 

    protected async Task Process(Message msg) 
    { 
     **UNBLOCKED CODE** 

     using (await _mutex.LockAsync()) 
     { 
      await GetData(msg.Key); 
     } 

     **UNBLOCKED CODE** 
    } 
} 

お勧めはありますか?

+0

「キー」はどのようなタイプですか?それが文字列でない場合、 'lock'ステートメントで動作する可能性があります。 –

+0

はい、それは文字列です – Rikard

答えて

3

ただ1つのAsyncLockを持つ代わりに、完全な辞書を作成してください。メッセージキーは、辞書内のAsyncLockのキーです。一度に1つのキー処理を持つメッセージが1つだけであれば、ブロックされることはありません。私はさらにあなたのメッセージキーが文字列であると仮定しています。

private readonly Dictionary<string, AsyncLock> _mutexes = new Dictionary<string, AsyncLock>(); 

private AsyncLock GetMutex(string key) 
{ 
    lock (_mutexes) 
    { 
     AsyncLock mutex; 
     if (!_mutexes.TryGetValue(key, out mutex)) 
     { 
      // no mutex yet, create a new one 
      mutex = new AsyncLock(); 
      _mutexes.Add(key, mutex); 
     } 
     return mutex; 
    } 
} 

.... 
protected async Task Process(Message msg) 
{ 
    using (await GetMutex(msg.Key).LockAsync()) 
    { 
     ... 
    } 
} 

キーのミューテックスが既に存在する場合、すべての可能性にGetMutex方法は、さらに、例えば、ロック操作を避けるために最適化することができることに注意してください最初にContainsKeyでチェックし、そうでない場合はロックして追加します。しかし、その最適化が100%スレッドセーフであるかどうかはわかりませんので、ここには含めませんでした。

JasonのConcurrentDictionaryのコメントに触発されて、私はGetMutexのConcurrentDictionaryバージョンのコードを提供することに決めました。私は、スレッドセーフではない辞書へのアクセスを最適化することについて、私が上に述べたことを試してみることはお勧めしません。

ConcurrentDictionary<string, AsyncLock> _mutexes = new ConcurrentDictionary<string, AsyncLock>(); 

private AsyncLock GetMutexes(string key) 
{ 
    return _mutexes.GetOrAdd(key, s => { return new AsyncLock(); }); 
} 
+0

私はConcurrentDictionaryを認識しておらず、その使用法を調べました。私が知ることから、ConcurrentDictionaryで2つの操作を行う必要があります。最初にTryGetValue、GetOrAddに失敗した場合(GetOrAddだけを呼び出した場合は、すべてのルックアップでAsyncLockを作成したくないため)。 – Christoph

+0

ロックしないで、通常の 'Dictionary'には何も操作したくないです。あるスレッドがロックを取得し、別のスレッドがキーを見つけようとしている間に辞書に項目を追加する場合を考えてみましょう。 –

+0

そしてRE: 'ConcurrentDictionary'には' GetOrAdd'の[overload](https://msdn.microsoft.com/en-us/library/ee378677%28v=vs.110%29.aspx)があります。新しい 'TValue'インスタンスの作成を委譲します。そのオーバーロードを使用すると、すべての呼び出しで新しいAsyncLockを作成する必要がなくなります。私は 'GetOrAdd(TKey、TValue)'オーバーロードの主な使用例はTValueが値型であると考えています。 –