Skip to content
Go back

【C#多线程面试必备】锁机制与异步陷阱,你真的会用Lock吗?

Published:  at  03:50 PM

【C#多线程面试必备】锁机制与异步陷阱,你真的会用Lock吗?

引言:一次尴尬的面试经历

“我因为不知道C#锁的工作原理而在一次面试中失败了。读完这篇文章,保证你不会重蹈覆辙。”
——一位开发者的自白

多线程编程对于许多软件开发者来说,既是晋升高阶的必经之路,也是每次面试绕不开的考点。特别是当你面对涉及资源争用、并发控制的问题时,一个小小的锁机制理解不到位,可能就会导致“当场翻车”。

今天,我们就从一个真实的面试场景出发,聊聊C#中的锁与异步操作,拆解那些让人头疼的陷阱,并带你走出误区!


为什么要使用锁?保护共享资源&并发控制

在多线程环境下,如果多个线程同时访问某个资源(比如全局变量、文件、数据库连接等),就有可能出现数据错乱或竞争条件(Race Condition)。因此,“加锁”成了并发编程绕不开的话题。

常见需求:

  1. 保护昂贵资源
    比如防止同一时间多次写入数据库。

  2. 实现并发控制
    限制某段代码同一时刻只能有固定数量的线程访问。


面试现场:lock语句失灵了!

假设你遇到了这样一道题:

此时,bug就悄然埋下了……💥

lock语句为啥和async不兼容?

lock本质上是对一个对象加独占锁,保证一段代码块在同一时刻只被一个线程访问。但在async/await异步方法中,代码执行会被挂起,然后切换到其他线程继续执行,这样lock保护的作用范围就被打破了!

也就是说:


正确姿势:用异步同步原语解决

那么,在C#中怎么优雅地处理异步方法中的并发问题?

常见解决方案

1️⃣ SemaphoreSlim —— 最推荐的异步同步工具

private static SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);

public async Task MyAsyncMethod()
{
    // 等待进入临界区,有timeout防止死锁
    await _semaphore.WaitAsync(TimeSpan.FromSeconds(5));
    try
    {
        // 临界区代码
        await DoSomethingAsync();
    }
    finally
    {
        // 一定要释放,否则会死锁!
        _semaphore.Release();
    }
}

要点:

2️⃣ 其他常见同步原语

结论:在异步场景下推荐用SemaphoreSlim!


总结与实用建议

  1. 牢记:lock不能用于async/await方法!
  2. 遇到并发资源保护,优先考虑SemaphoreSlim。
  3. 写多线程/异步代码时,养成try-finally释放锁的好习惯。
  4. 如有必要,为WaitAsync设置合理的超时时间。


Previous Post
.NET开发中的适配器模式:让系统集成更丝滑的秘密武器
Next Post
用C#和EF Core实现高性能SQL批量插入全攻略