我正在多线程环境中对SQL事务运行一些测试。我试图通过在paralel中运行的2个线程的循环中执行单个存储过程来生成死锁。我的两个线程在启动时使用相同的方法,它连续执行一个存储过程:
using (TestDataContext db = new TestDataContext())
{
while (true)
{
db.DeadLocking();
}
}
有人可以给出一个“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
从paralel中的两个线程运行它应该将这些线程锁定在彼此之上。我做错了什么?
更新:上面的过程确实会导致死锁,但是,至少需要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隔离级别上。
答案 0 :(得分:0)
考虑在select中要求更新锁。 WITH(UPDLOCK)。
确保SELECT已经更新记录。
答案 1 :(得分:0)
要创建死锁,您肯定需要两个不同的过程,或至少需要以不同顺序获取锁的执行分支。
事实上,确保获取锁定的严格命令是防止死锁的众所周知的方法。
因此,您将需要两个不同的锁A和B,例如在两个不同的表上。然后一个线程将尝试锁定A 然后 B,而另一个线程将尝试锁定B 然后 A.只有这样才有机会创建一个死锁。
如果您想增加在运行时实际发生死锁的可能性,则需要一些延迟,例如:
lock A
delay5Seconds
lock B
delay5Seconds
unlock B
unlock A
和
lock B
delay5Seconds
lock A
delay5Seconds
unlock A
unlock B
这允许其他线程在正确的时间点到达死锁。
为了确保每次都产生死锁,必须创建一个机制来同步两个线程执行,比如
lock A
wait for thread #2 to lock B
lock B
...
和
lock B
wait for thread #1 to lock A
lock A
...
编辑:
发现这个似乎有关的问题:Confused about UPDLOCK, HOLDLOCK
从中推断你可以尝试这样:
BEGIN TRANSACTION
SELECT * FROM Test.dbo.Records WHERE RecordId = 1 WITH (UPDLOCK, HOLDLOCK)
WAITFOR DELAY '00:00:10'
SELECT * FROM Test.dbo.Records WHERE RecordId = 999999 WITH (UPDLOCK, HOLDLOCK)
WAITFOR DELAY '00:00:10'
COMMIT TRANSACTION
和
BEGIN TRANSACTION
SELECT * FROM Test.dbo.Records WHERE RecordId = 999999 WITH (UPDLOCK, HOLDLOCK)
WAITFOR DELAY '00:00:10'
SELECT * FROM Test.dbo.Records WHERE RecordId = 1 WITH (UPDLOCK, HOLDLOCK)
WAITFOR DELAY '00:00:10'
COMMIT TRANSACTION
原则上,这应该在所有事务隔离级别上执行。 (您在两个线程中都使用显式锁,因此DBMS会忽略它。) 但是,如果来自同一个表的两个连续选择实际上将获得两个单独的锁(每个受影响的记录一个),或者如果DBMS可能只是决定锁定整个表,或者至少它的单一范围,因此只有一个锁被有效地保持。因此,为了确保您在不同时间点实际获得两个锁定,您实际上可能希望将两个选择分散到两个不同的表。