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

Monitor.TryEnter始终返回true未生效,异步场景下如何解决?

关于Monitor.TryEnter在异步方法中失效的问题

测试代码

public class Test
{
    public async Task Lock(object _lock)
    {
        if (!Monitor.TryEnter(_lock)) {
            Console.WriteLine("locked");
            return;
        }
        Console.WriteLine("entered");
        await Task.Delay(99999);
        Monitor.Exit(_lock);
        Console.WriteLine("exited");
    }
}
static async Task Main()
{
    var _lock= new object();
    var test = new Test();
    do {
        test.Lock(_lock);
        Thread.Sleep(999);
    } while (true);
}

预期输出

entered
locked
locked
...
exited
entered
locked
locked
...
exited

实际输出

entered
entered
entered
...
exited

问题原因

核心问题是Monitor是基于线程的同步原语,它的锁与持有锁的线程绑定。异步方法执行到await Task.Delay(...)时,当前线程会被释放,后续代码会在任意线程上恢复执行,这就导致:

  1. 第一次调用Monitor.TryEnter成功获取锁,此时锁与执行这段代码的线程绑定;
  2. await后线程被释放,后续的Monitor.Exit会在另一个线程执行,但Monitor只认最初持有锁的线程;
  3. Main方法中调用test.Lock(_lock)时没有await,每次调用都是在Main线程执行Monitor.TryEnter——而Main线程从来没持有过这个锁,所以每次都能成功获取,自然会连续输出"entered"。

解决方案

方案1:用SemaphoreSlim实现异步锁(推荐)

SemaphoreSlim支持异步等待,是专门为异步场景设计的同步原语,完全适配你的需求:

public class Test
{
    public async Task Lock(SemaphoreSlim semaphore)
    {
        // WaitAsync(0)表示立即返回,等价于Monitor.TryEnter的非阻塞行为
        if (!await semaphore.WaitAsync(0)) 
        {
            Console.WriteLine("locked");
            return;
        }
        Console.WriteLine("entered");
        await Task.Delay(99999);
        semaphore.Release();
        Console.WriteLine("exited");
    }
}
static async Task Main()
{
    // 初始计数1、最大计数1,等价于互斥锁
    var semaphore = new SemaphoreSlim(1, 1); 
    var test = new Test();
    do {
        // 这里必须await,避免堆积大量未完成的任务
        await test.Lock(semaphore); 
        Thread.Sleep(999);
    } while (true);
}

方案2:避免在持有Monitor锁时执行await(仅适合特定场景)

如果一定要用Monitor,必须保证持有锁的期间不释放线程,也就是把await移出锁的范围,但这种方式会让异步操作脱离锁的保护,不符合你的预期逻辑,仅作参考:

public async Task Lock(object _lock)
{
    bool lockTaken = false;
    try
    {
        Monitor.TryEnter(_lock, ref lockTaken);
        if (!lockTaken)
        {
            Console.WriteLine("locked");
            return;
        }
        Console.WriteLine("entered");
        // 锁只保护到这里,异步操作在锁外执行
    }
    finally
    {
        if (lockTaken)
        {
            Monitor.Exit(_lock);
        }
    }
    await Task.Delay(99999);
    Console.WriteLine("exited");
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 10:51:21