簡體   English   中英

具有單個存儲過程和多個線程的死鎖

[英]Deadlock with single stored procedure and multiple threads

我正在多線程環境中對SQL事務運行一些測試。 我試圖通過在2個並行運行的線程中循環執行單個存儲過程來生成死鎖。 我的兩個線程在啟動時都使用相同的方法,該方法連續執行一個存儲過程:

using (TestDataContext db = new TestDataContext())
{
    while (true)
    {
        db.DeadLocking();
    }
}

有人可以舉一個“ DeadLocking”存儲過程的例子嗎?在這種情況下,該過程將可靠地產生死鎖。 它必須使用事務(單個或多個)。 我已經研究了很多,並看到了許多如何在sql中生成死鎖的示例,但是,在我的代碼中沒有一個起作用。 請幫忙。

更新:遵循Marc的建議,我嘗試使用此程序無濟於事:

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隔離級別上都會發生。

考慮在選擇中要求更新鎖定。 與(UPDLOCK)。

這樣可以確保SELECT已更新記錄。

要創建死鎖,您肯定需要兩個不同的過程,或者至少是執行分支,它們以不同的順序獲取鎖。

實際上,確保嚴格掌握獲取鎖是防止死鎖的眾所周知的方法。

因此,您將需要兩個不同的鎖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
...

編輯:

找到了一個似乎與之相關的SO問題: 對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可能只是決定鎖定整個表或表的至少一個范圍,以便僅有效地持有一個鎖定。 因此,為確保您確實在不同的時間點獲得了兩個鎖,您實際上可能希望將兩個選擇散布到兩個不同的表中。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM