5

いくつかのエンティティ フレームワーク モデルのいくつかのフィールドのアトミック操作チェック値を実行し、その値が 0 の場合はそれを増やす必要があります。

私はトランザクションについて考えました。

bool controlPassed = false;
using (TransactionScope scope = new TransactionScope())
{
    var model = ...ModelEntities.first_or_default(...)
    if (model.field == 0){
        ++model.field;
        ...saveChanges();
        controlPassed = true;
    }
    scope.Complete();
}
if (controlPassed)
{
    ...
    using (TransactionScope scope = new TransactionScope())
    {
        --model.field;
        ...saveChanges();
        scope.Complete();
    }
}

もちろん、try catch などのすべて。

私の質問は次のとおりです。

チェックするのは本当に難しいです。マルチスレッドアプリケーションがあります。

2 つ以上のスレッドが制御を渡す可能性はありfield == 0ますか (確認して増やしてください)。

データベース (データベース、テーブル、行、フィールド) でブロックされるのは誰ですか?

2 つ以上のスレッドをcontrolPassed同時にセクションに入れることはできません。

4

1 に答える 1

10

2 つ以上のスレッドが制御を渡す可能性はありますか (フィールド == 0 を確認して増加させます)。

トランザクションがSerializableあります (これが のデフォルトですTransactionScope)。これは、field == 0 の 2 つのスレッドが存在する可能性があることを意味しますが、最初のスレッドのトランザクションがフィールドの共有ロックを保持し、2 番目のスレッドのトランザクションが同じフィールドの別の共有ロックを保持するため、その直後にデッドロックが発生します。これらのトランザクションは、他のトランザクションの共有ロックによってブロックされているため、変更を保存するためにロックを排他的にアップグレードすることはできません。RepeatableRead分離レベルでも同じことが起こると思います。

分離レベルを(MS SQL Server を使用していない場合ReadCommittedのデフォルト) に変更すると、答えは再び「はい」になりますが、EF はテーブル ヒントなしで通常の選択を使用するため、今回はデッドロックは発生しません。つまり、選択が完了したときにレコードのロックは保持されません。 . トランザクションがコミットまたはロールバックされるまで、変更 (変更) の保存のみがレコードをロックします。SaveChangesTransactionScope

select を使用してトランザクションでレコードをロックReadCommittedするには、ネイティブ SQL クエリとUPDLOCK(選択中に更新のためにレコードをロックし、トランザクションが終了するまで保持する) テーブル ヒントを使用する必要があります。EF クエリは、テーブル ヒントをサポートしていません。

編集:悲観的並行性に関する長い記事を書きました。この記事では、ソリューションが機能しない理由と、正しく機能させるために何を変更する必要があるかを説明しています。

于 2012-09-26T12:31:32.933 に答える