C# 使用LINQtoSQL,如何在多个进程之间划分队列表中的记录?

C# 使用LINQtoSQL,如何在多个进程之间划分队列表中的记录?,c#,sql-server,linq-to-sql,concurrency,C#,Sql Server,Linq To Sql,Concurrency,我使用SQL Server数据库表作为工作队列 我有一个作家和多个读者 写入程序为队列表插入生成新记录 每个读卡器查找处于特定状态的记录,选择要使用的记录,获取一批记录,将每个记录标记为已拥有的更新,对其进行处理,然后将所有已处理的记录标记为已完成或更新失败。记录的工作流程很简单:状态从“A”变为“已添加”,从“B”变为“正在处理”,从“C”变为“完成”,或从“F”变为“失败”。每个记录的所有者在状态“A”时从未设置变为唯一标识符,标识其余步骤的读取器。唯一标识符一个十进制加上状态nchar1再

我使用SQL Server数据库表作为工作队列

我有一个作家和多个读者

写入程序为队列表插入生成新记录

每个读卡器查找处于特定状态的记录,选择要使用的记录,获取一批记录,将每个记录标记为已拥有的更新,对其进行处理,然后将所有已处理的记录标记为已完成或更新失败。记录的工作流程很简单:状态从“A”变为“已添加”,从“B”变为“正在处理”,从“C”变为“完成”,或从“F”变为“失败”。每个记录的所有者在状态“A”时从未设置变为唯一标识符,标识其余步骤的读取器。唯一标识符一个十进制加上状态nchar1再加上一个额外的批次id int(标识写入程序的较大批次)是处理中的三个关键字段。此外,该表还有一个timestamp字段,LINQ使用该字段进行并发性检查

我需要防止由于两个读卡器选择相同的记录进行处理而导致锁定延迟和更新失败。作为处理工作的一部分,进行web服务调用可能需要不确定的时间才能完成。我们通过事务向web服务供应商付款,所以我不想打电话,然后找出处理相同记录的另一个进程。这样的错误将花费我们数千美元

我读了这篇文章:

这看起来很有希望,但我决定使用LINQtoSQL。我读了这篇文章:

后者说,除了粗略地包装SQL过程之外,没有办法告诉LINQ使用UPDLOCK。这是因为LINQ使用乐观并发,而这些特性假定了悲观锁定

那么,如何使用LINQ支持的特性来解决这个问题呢?我的应用程序是多线程的,但是我可以在同一个队列表上运行多个实例,所以我不能只让一个调度器将记录传递给每个使用者线程

我正在使用.NET4.0。我的应用程序是一个普通的旧Windows控制台应用程序。该解决方案应与SQL Server 2005和SQL Server 2008 R2配合使用。我不想使用消息传递系统

更新:关于SO的进一步研究。找到这篇文章:

答案建议使用dispatcher线程,我排除了这一点

更新2:找到另一篇关于SO的文章:


这看起来很有希望。我需要执行直接SQL,但一旦我标记了我的记录并获得了ID,剩下的事情就可以在L2S中发生。

也许你可以在读者中采取一种更悲观的方法来处理出列过程

让读卡器接收下一个排队项目。然后让读取器尝试将该项目出列。读取器可以为出列调用中的项分配一个唯一的值,例如guid

然后,读取器可以尝试获取它已退出队列的队列项目。当它收到项目时,它可以比较它已分配的项目的guid。如果它们是相同的,那么读者就可以处理出队列


假设你的读者一次可以把一个项目排出来,这应该是可行的。我不确定当每个读者将多个项目排在队列中时,这是如何工作的。

这是我的答案。我将存储过程与DataContext关联以保留地址。它能够使用悲观并发在单个操作中选择、标记和返回记录

CREATE PROCEDURE dbo.kccsp_ReserveAddresses
      @BATCH_SIZE int,
      @BATCH_ID int,
      @PROCESS_TOKEN numeric(18,0)
  AS
      -- Reserve a group of addresses in the queue so that a consumer may process them,
      -- but all other consumers will leave them alone.
      -- The query hints are essential to prevent lock contention, 
      -- or concurrency errors from multiple processors handling the same address.
      update TOP (@BATCH_SIZE) 
          KCC_GeoCodingAddressQueue WITH (ROWLOCK, READPAST, UPDLOCK)
      set 
          [Process Status] = 'B', 
          [Process Token] = @PROCESS_TOKEN
      output INSERTED.*
      where [Process Status] = 'A' and [Process Token] = 0
为此,我将该过程拖到dbml设计器中的结果表上。这将导致结果集与实体类相同,并在标记所有已分配记录的同时返回这些记录。SQL提示处理并发和锁争用

当我需要用C调用它时,我会这样做:

   int? batchSize = 50;
   int? batchIdToReserve = Batch.GeoBatchID;
   decimal? processorId = someId;
   var addresses = Context.kccsp_ReserveAddresses(batchSize, batchIdToReserve, processorId);

交易与此相关。作为补充说明,有什么理由不使用实体框架,因为LINQ2SQL几乎已经死了@我不使用EF的第一个原因是无知,第二个原因是工作期限。EF的学习曲线是什么样的?EF的学习曲线很糟糕:在您的情况下,我可能会使用存储过程并将其连接到LINQ到SQL,而不是在代码中使用事务。@Stillgar我是否察觉到关于学习曲线的一些微妙讽刺?lol.您的需求要求锁在数据库中完成任何细节。我不确定您反对使用存储过程的原因。值得调查。我愿意一次排一个队列,然后为web服务调用对它们进行批处理。当我一次发送50条记录时,我的网络性能最好。100条记录偶尔会超过某个URL长度限制。该服务只使用GET,不使用POST。不过,我不确定如何使用这种方法解决故障转移问题。也许时间戳可以用来支持故障转移。我的主批处理表上有一个DateTime字段。当它超时时,我释放队列表中 已经为那批货保留了。这允许我重新启动部分完成的批处理并恢复处理。