2013-03-27 37 views
1

我会尝试用简化的控制台应用程序示例来解释问题,但是真正的项目是ASP.NET MVC3应用程序。如何分离并行请求?

具有如下表:

enter image description here

想象以下场景:

  1. 用户创建一个报告(在TestReport线,其中Text是报告string内容,并Ready是一个bool的标志,说,如果报告准备好处理);默认Ready设置为false,即未准备好。
  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

为什么不在报表上添加version列?任务从追踪当前版本开始,当任务结束时,如果版本与追踪的版本相同,则操作正常,否则失败。如果操作显示正常,请将版本更新至版本+1。这是一种乐观的锁定;这暗示着可能会发生冲突,但它们并不常见。

UPDATE

如果使用SQL linqto也许你可以在参数UpdateCheck[Column(UpdateCheck=UpdateCheck.Always)] 检查这可能是你的情况来处理并发有用。

+0

这种方法会导致几乎每个表都添加'version'列。在真正的项目中,任务需要检查不同的表格(每个表格有几行)。锁定所有涉及的线路似乎是一项相当复杂的任务。除此之外,当结果可能不正确时,还需要为每种情况编写回滚操作。当然,我很乐意避免所有这些事情。 – horgh 2013-03-27 06:37:51

+0

乐观的方法也可以通过检查所有字段仍然与您在书写之前阅读的相同。你在使用任何OR/M吗? – 2013-03-27 08:26:59

+0

我正在使用LinqToSql – horgh 2013-03-27 08:30:07