我使用 SQL Server 数据库表作为工作队列。
我有一个作家和多个读者。
写入器为队列表 (INSERT) 生成新记录。
每个读取器查找处于特定状态的记录(SELECT)进行消费,获取一批记录,将每条记录标记为拥有(UPDATE),处理它们,然后将所有处理的记录标记为完成或失败(UPDATE)。记录的工作流程很简单:状态从“A”表示已添加,到“B”表示正在处理,再到“C”表示完成或“F”表示失败。并且每条记录的所有者从处于状态“A”时的未设置变为唯一标识符,用于识别剩余步骤的阅读器。唯一标识符(一个十进制)加上状态(nchar(1))加上一个额外的批次 id(int),用于标识写入器的较大批次是处理中的三个关键字段。此外,该表还有一个时间戳字段,LINQ 使用该字段进行并发检查。
我需要防止由于两个读者选择相同的记录来处理而导致锁定延迟和更新失败。作为处理工作的一部分,会进行一个 Web 服务调用,该调用可能需要不确定的时间才能完成。我们通过交易向 Web 服务供应商付款,所以我不想打电话,然后找出另一个处理相同记录的进程。此类错误将使我们损失数千美元。
我读了这篇文章:
使用 READPAST 和 UPDLOCK 处理 SQL Server 中的数据队列
这看起来很有希望,但我决定使用 LINQ to SQL。我读了这篇文章:
后者说没有办法告诉 LINQ 使用 UPDLOCK 除非粗略地包装一个 SQL 过程。这是因为 LINQ 使用乐观并发,而这些功能假设是悲观锁定。
那么如何使用 LINQ 支持的特性来解决这个问题呢?我的应用程序是多线程的,但我可以针对同一个队列表运行多个实例,因此我不能只让一个调度程序将记录交给每个消费者线程。
我正在使用 .NET 4.0。我的应用程序是一个普通的旧 Windows 控制台应用程序。该解决方案应适用于 SQL Server 2005 和 SQL Server 2008 R2。我不想使用消息传递系统。
更新:对 SO 的进一步研究。找到这篇文章:
答案建议有一个调度程序线程,我排除了。
更新 2:在 SO 上找到另一篇文章:
这看起来真的很有希望。我需要直接执行 SQL,但是一旦我标记了我的记录并获得了我的 ID,其余的都可以在 L2S 中发生。