c# - 这个简单的代码产生了死锁。包括简单的示例程序

标签 c# sql sql-server-2012

代码,注意值的顺序是不同的。所以它在锁定行之间交替:

static void Main( string[] args )
        {
            List<int> list = new List<int>();

            for(int i = 0; i < 1000; i++ )
                list.Add( i );

            Parallel.ForEach( list, i =>
            {
                using( NamePressDataContext db = new NamePressDataContext() )
                {
                    db.ExecuteCommand( @"update EBayDescriptionsCategories set CategoryId = Ids.CategoryId from EBayDescriptionsCategories 
                        join (values (7276, 20870),(240, 20870)) as Ids(Id,CategoryId) on Ids.Id = EBayDescriptionsCategories.Id" );

                    db.ExecuteCommand( @"update EBayDescriptionsCategories set CategoryId = Ids.CategoryId from EBayDescriptionsCategories 
                        join (values (240, 20870),(7276, 20870)) as Ids(Id,CategoryId) on Ids.Id = EBayDescriptionsCategories.Id" );
                }

            } ); 
        }

表定义:

CREATE TABLE [dbo].[EDescriptionsCategories](
    [CategoryId] [int] NOT NULL,
    [Id] [int] NOT NULL,
 CONSTRAINT [PK_EDescriptionsCategories] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)

异常(exception):

Transaction (Process ID 80) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.

该代码仅适用于 WITH (TABLOCK) 提示。是否可以不锁定整个表只是为了并行更新这两行?

最佳答案

您的两个语句以不同的顺序获取行锁。这是死锁的经典案例。您可以通过确保获取锁的顺序始终处于某种全局顺序(例如,按 ID 排序)来解决此问题。您可能应该将两个 UPDATE 语句合并为一个语句,并在将其发送到 SQL Server 之前对客户端上的 ID 列表进行排序。对于许多典型的 UPDATE 计划,这实际上工作正常(但不能保证)。

或者,您添加重试逻辑以防检测到死锁 (SqlException.Number == 1205)。这更优雅,因为它不需要更深入的代码更改。但死锁会对性能产生影响,因此只在死锁率较低时才这样做。

如果您的并行处理生成大量更新,您可以将所有这些更新INSERT 到一个临时表中(可以同时完成),完成后执行一个大的UPDATE 将所有单独的更新记录复制到主表。您只需更改示例查询中的连接源。

关于c# - 这个简单的代码产生了死锁。包括简单的示例程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20239143/

相关文章:

c# - 如何使用 Mediator 配置 MassTransit 来发布消息?

c# - 邮件在创建后过早启用 AD 帐户

c# - 在循环中顺序调用异步方法

mysql - 缺少 NULL 答案的 SQL JOIN 问题和答案

java - 外键未获取主键的 Id 值打印 NULL

sql - 在 SQL Server 中使用已删除记录中的值

c# - 将 C# 对象存储在 SQL Server 列中

MySQL 使用 IF 语句更新 2 列

sql-server - 需要从 AUDIT 表中过滤特定列的记录

sql-server - 缺口和岛屿?或不?