You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决数组场景下的多线程死锁问题(C#面试真题)

死锁面试题解法

基础固定顺序双锁死锁解决

原题代码如下:

lock(a) {
    lock(b) {
        // do smth
    }
}

lock(b) {
    lock(a) {
        // do smth
    }
}

该场景死锁的核心原因是两个线程的锁获取顺序相反,形成循环等待。你回答的「统一锁获取顺序」是正确解法:提前约定所有线程都按固定规则(如锁ID升序、哈希值升序)获取锁,即可破坏循环等待条件,避免死锁。

随机双锁场景死锁自动解决方案

原题伪代码如下:

function void foo(T[] arr) {
    lock(arr[randint(arr.length)]) {
        lock(arr[randint(arr.length)]) {
             // do smth
        }
    }
}

var arr = new arr{...}

async () => {
    foo(arr)
    foo(arr)
}

该场景的问题是锁获取顺序完全随机,仍可能出现线程1持有锁A等锁B、线程2持有锁B等锁A的死锁,本质还是锁获取顺序不一致的问题,有两类解决思路:

通用解法:自动排序锁获取顺序

不需要业务手动干预顺序,代码内部自动对要获取的两个锁做排序,永远先拿排序靠前的锁,再拿靠后的锁,即可保证所有线程的锁获取顺序一致。修改后的伪代码如下:

function void foo(T[] arr) {
    var lockObj1 = arr[randint(arr.length)];
    var lockObj2 = arr[randint(arr.length)];
    // 特殊处理两次随机到同一个对象的场景,避免重复加锁
    if (lockObj1 == lockObj2) {
        lock(lockObj1) {
            // do smth
        }
        return;
    }
    // 按哈希值排序确定顺序,也可使用自定义的锁唯一ID排序
    var firstLock = lockObj1.GetHashCode() > lockObj2.GetHashCode() ? lockObj2 : lockObj1;
    var secondLock = lockObj1.GetHashCode() > lockObj2.GetHashCode() ? lockObj1 : lockObj2;
    lock(firstLock) {
        lock(secondLock) {
            // do smth
        }
    }
}

该方案性能更高,无重试开销,要求锁对象存在可比较的唯一标识即可。

C#专属解法:使用Monitor.TryEnter超时回退重试

C#中lock关键字是Monitor.Enter的语法糖,你可以手动调用Monitor.TryEnter指定超时时间,如果拿不到第二个锁就主动释放已持有的锁,回退后重试,破坏死锁的持有等待条件。示例代码如下:

void Foo<T>(T[] arr) where T : class
{
    while (true)
    {
        var lock1 = arr[Random.Shared.Next(arr.Length)];
        var lock2 = arr[Random.Shared.Next(arr.Length)];
        // 两次随机到同一个对象的特殊处理
        if (lock1 == lock2)
        {
            lock (lock1)
            {
                // 执行业务逻辑
                return;
            }
        }
        bool lock1Taken = false;
        bool lock2Taken = false;
        try
        {
            // 尝试获取第一个锁,超时时间可自行调整
            Monitor.TryEnter(lock1, TimeSpan.FromMilliseconds(100), ref lock1Taken);
            if (!lock1Taken) continue;
            // 尝试获取第二个锁
            Monitor.TryEnter(lock2, TimeSpan.FromMilliseconds(100), ref lock2Taken);
            if (!lock2Taken) continue;
            // 两个锁都获取成功,执行业务逻辑
            return;
        }
        finally
        {
            // 释放已拿到的锁
            if (lock2Taken) Monitor.Exit(lock2);
            if (lock1Taken) Monitor.Exit(lock1);
        }
    }
}

该方案不需要提前给锁对象设置可比较的唯一标识,适配性更强,极端情况下可能出现活锁,可通过增加随机重试延迟缓解。

内容的提问来源于stack exchange,提问作者Geara0

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 04:36:03