1

簡単なコンソール アプリケーションの例で問題を説明しようとしますが、実際のプロジェクトは ASP.NET MVC3 アプリケーションです。

次のテーブルがあります。

ここに画像の説明を入力

次のシナリオを想像してください。

  1. ユーザーがレポートを作成します (はレポートの内容、TestReportはフラグで、レポートを処理する準備ができているかどうかを示します)。デフォルトでは に設定されています。つまり、準備ができていません。TextstringReadyboolReadyfalse
  2. ユーザーはレポートを処理したいので、レポートを送信します。Readyここに設定されてtrueいます。

レポートがまだ処理されていない場合、システムはレポートを取り消す機会を与えます。そのため、レポートがリコールされると、バックReadyに設定されfalseます。逆に、レポートが処理されると、TestReportRefその によってレポートを参照する の行Idが作成されます。

同時に想像してみてください

  1. ユーザーはレポートを取り消したいと考えています。
  2. レポートがプロセス リストに追加されます。

これが同時に発生するとすぐに、エラーが発生する可能性があります。つまり、レポートにはReady==が含まfalseれ、 で参照されTestReportRefます。

これがどのように発生するかを示す簡単なコンソールの例を次に示します。

var dc = new TestDataContext('my connection string');
dc.TestReport.InsertOnSubmit(new TestReport
{
    Text = "My report content",
    Ready = true //ready at once
});
dc.SubmitChanges();

Action recallReport = () =>
{
    var _dc = new TestDataContext(cs);
    var report = _dc.TestReport.FirstOrDefault(t => t.Ready);
    if (report != null && !report.TestReportRef.Any())
    {
        Thread.Sleep(1000);
        report.Ready = false;
        _dc.SubmitChanges();
    }
};

Action acceptReport = () =>
{
    var _dc = new TestDataContext(cs);
    var report = _dc.TestReport.FirstOrDefault(t => t.Ready);
    if (report != null && !report.TestReportRef.Any())
    {
        Thread.Sleep(1000);
        _dc.TestReportRef.InsertOnSubmit(new TestReportRef
        {
            FK_ReportId = report.Id
        });
        _dc.SubmitChanges();
    }
};

var task1 = new Task(recallReport);
var task2 = new Task(acceptReport);

task1.Start();
task2.Start();

task1.Wait();
task2.Wait();

foreach (var t in dc.TestReport)
{
    Console.WriteLine(string.Format("{0}\t{1}\t{2}", t.Id, t.Text, t.Ready));
}

foreach (var t in dc.TestReportRef)
{
    Console.WriteLine("ref id:\t" + t.FK_ReportId);
}

Thread.Sleep(1000);タスクが同じ状況を確実にチェックするように追加されます。

与えられた例はぎこちなく聞こえるかもしれませんが、私が扱っている問題を説明するはずです。

どうすればこれを回避できますか? リポジトリをシングルトンにするのは良い考えではないようです。共有ミューテックス (すべての Web 要求に 1 つ) を使用して、書き込み操作のみを分離しますか? または、この種のシナリオで使用する必要があるパターンはありますか?


これは、私が持っているシナリオの 1 つの単純化された例にすぎません。ただし、同様の不一致が発生するシナリオがいくつかあります。このような交差点を不可能にするのが最善だと思います。

4

1 に答える 1

1

versionReport テーブルに列を追加しないのはなぜですか? タスクは現在のバージョンを追跡することによって開始され、タスクが終了すると、バージョンが追跡されたものと同じ場合、操作は問題なく、そうでない場合は失敗します。正常に動作する場合は、バージョンを +1 に更新します。これは一種の楽観的ロックです。競合が発生する可能性があることを暗黙のうちに想定していますが、それほど頻繁ではありません。

アップデート

linqto sql を使用している場合は、パラメーターUpdateCheck [Column(UpdateCheck=UpdateCheck.Always)]でチェックできる可能性があります。 これは、ケースで同時実行を処理するのに役立ちます。

于 2013-03-27T06:21:27.333 に答える