如何分离并行请求?

时间:2013-03-27 06:07:35

标签: c# asp.net asp.net-mvc-3 linq-to-sql

我将尝试使用简化的控制台应用程序示例解释该问题,但真正的项目是ASP.NET MVC3应用程序。

拥有以下表格:

enter image description here

想象以下场景:

  1. 用户创建报告(TestReport中的一行,其中Text是报告string内容,Readybool标志,说,如果报告已准备好处理);默认情况下,Ready设置为false,即尚未就绪。
  2. 用户希望处理报告,因此他提交报告;此处Ready设置为true
  3. 如果报告尚未处理,系统将提供回溯报告的机会。因此,在调用报告时,Ready将设置为false。相反,在处理报告时,会在TestReportRef中创建一条引用其Id报告的行。

    现在想象一下

    1. 用户想要回忆报告;
    2. 报告已添加到流程列表中;
    3. 只要同时发生这种情况,就可能发生错误。这是报告将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 个答案:

答案 0 :(得分:1)

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

<强>更新

如果您使用的是linqto sql,可以查看参数UpdateCheck [Column(UpdateCheck=UpdateCheck.Always)] 这对于处理你的并发性很有用。