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 请求)来分隔写操作吗? 或者在这种情况下我应该使用什么模式?


这只是我所拥有的场景之一的简化​​示例。但是,在某些情况下,它可能会遇到类似的差异。我猜最好的办法是让这种交叉点成为不可能。

4

1 回答 1

1

为什么不在version报告表上添加一列?任务从跟踪当前版本开始,任务结束时,如果版本与跟踪的版本相同,则操作正常,否则失败。如果操作正常,请将版本更新为版本 +1。这是一种乐观锁;这隐含地假设可能会发生冲突,但它们并不那么频繁。

更新

如果您使用的是 linqto sql,也许您可​​以检查参数UpdateCheck [Column(UpdateCheck=UpdateCheck.Always)] 在您的情况下这对于处理并发很有用。

于 2013-03-27T06:21:27.333 回答