【发布时间】:2014-11-24 17:49:06
【问题描述】:
我正在多线程环境中对 SQL 事务进行一些测试。我试图通过在一个循环中从并行运行的 2 个线程执行单个存储过程来生成死锁。我的两个线程在启动时都使用相同的方法,它连续执行一个存储过程:
using (TestDataContext db = new TestDataContext())
{
while (true)
{
db.DeadLocking();
}
}
有人可以举一个“死锁”存储过程的例子,在这种情况下会可靠地产生死锁。它必须使用事务(单个或多个)。我已经研究了很多,并看到了许多关于如何在 sql 中生成死锁的示例,但是,它们都没有在我的代码中起作用。请帮忙。
更新:按照 Marc 的建议,我尝试了这个 sproc,但无济于事:
CREATE PROCEDURE [dbo].[DeadLocking]
AS
BEGIN
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
SET NOCOUNT ON
BEGIN TRANSACTION
DECLARE @val varchar(1)
SELECT @val = Record FROM Test.dbo.Records WHERE RecordId = 1
UPDATE Test.dbo.Records SET Record = @val WHERE RecordId = 1
COMMIT TRANSACTION
END
从两个线程并行运行它应该将这些线程锁定在彼此上。我做错了什么?
更新:上述过程确实会导致死锁,但是,它至少需要 3 个线程而不是 2 个(不知道为什么,可能需要 2 个但也需要永远)。有趣的是,这也会导致死锁:
CREATE PROCEDURE [dbo].[DeadLocking]
AS
BEGIN
SET NOCOUNT ON
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
UPDATE Test.dbo.Records SET Record = 1 WHERE RecordId = 1
END
我猜这是因为存储过程本身在幕后实现了某种事务逻辑。如果有人有更多关于为什么会发生的信息,请分享。 请注意,死锁仅发生在 UPDATE 上,不会发生在 SELECT 上。这发生在 SERIALIZABLE 和 REPEATABLE READ 隔离级别上。
【问题讨论】:
-
经典的死锁方法:使用“可序列化”隔离级别,让 spid A 对某些数据进行读锁(没有
updlock);让 spid B 对 相同的数据 进行读取锁定;现在 spid A 如何对相同的数据进行写锁定(尝试更改列),并让 spid B 尝试执行相同操作。他们现在都陷入了僵局。 -
@Marc,我认为简单的 SELECT 不会在一行上获得读锁,对吗?
-
在可序列化的隔离级别;是的:它会的。在大多数其他隔离级别:否
-
重新编辑:这是一个 线程竞赛 场景 - 除非您从外部手动编排它,否则您完全有可能永远看不到这里的冲突,除非你迭代它成千上万次。您可以做的一件事是在
SELECT和UPDATE之间添加WAITFOR延迟(可能是5 秒),这样两个SPID 在尝试读取之前就有更高的机会拥有读锁进行更新。 -
那里不需要被动攻击姿态
标签: c# sql sql-server multithreading stored-procedures