web-dev-qa-db-ja.com

複数のトランザクションを許可する競合状態。辞書内のオブジェクトをロックすると問題が発生しますか?

特定の金額のトランザクションを書き込むために呼び出されるMVC Web APIがあり、その金額はユーザーの残高に借方記入または貸方記入されます。これは、取引前残高(PreAmount)、金額、および取引後残高(PostAmount)を記録します。 SQL ServerのLAG関数とLEAD関数を使用した単純なクエリから、APIから追加されているトランザクションのインスタンスが「入力が速すぎる」ことが原因であることがわかりました(一部、リソースが不足しているため) IISとSQLが同じボックスで実行されています)。トランザクションが存在する場合、トランザクションが入力されないようにするメカニズムがありますが、メソッド呼び出しはは非常に迅速に呼び出され、その結果、競合状態のためにレコードが複数回追加されます。

これを修正しようとする方法は、Dictionary<long, object>。ここでの前提は、顧客のトランザクションを常に連続して入力することですが、複数の顧客の同時要求をロックしたくない。ディクショナリには顧客ID#が含まれ、値はロックできるオブジェクトになります。トランザクションが入力されようとしているときに、お客様ID#のエントリがあるかどうかを確認し、ない場合はそこに入力します)。

private static Dictionary<long, object> customerLocker = new Dictionary<long, object>();
private static object dictLocker = new object();

次に、残高を確認するか、トランザクションを入力する要求が要求されたときに、そのオブジェクトをロックして、必要な作業を実行します。

lock (dictLocker)
{
    if (!customerLocker.ContainsKey(customerID))
        customerLocker.Add(customerID, new object());
}
lock (customerLocker[customerID])
{
    // ... do the work needed for this customer
}

これは私が抱えている問題の良い解決策ですか? ディクショナリ内の要素をロックすると問題が発生しますか?試してみるつもりですが、これをまとめながら、このシナリオで他のユーザーからの入力を確認します。

4
Derreck Dean

lockの外の辞書を検索するのは安全ではありません。そうすると、別の辞書が同時に書き込みを行う可能性があり、読み取りが破損する可能性があります。 lockのキーinsideの値をフェッチする必要があります。

または、ConcurrentDictionaryへのアクセスをロックする代わりに、Dictionaryを使用して各顧客のオブジェクトを格納することもできます。これにより、次のように記述できます。

private static ConcurrentDictionary<long, object> customerLocker = 
    new ConcurrentDictionary<long, object>();
lock (customerLocker.GetOrAdd(customerID, () => new object()))
{
    // ... do the work needed for this customer
}
5
Servy