【问题标题】:C# Lock and Async MethodC# 锁定和异步方法
【发布时间】:2013-12-03 18:45:52
【问题描述】:

我不清楚(也找不到足够清楚的文档):在异步方法中使用 lock 关键字时:如果对象已经被阻塞,线程会被阻塞还是返回挂起状态的任务(不是阻塞线程,释放锁时返回)?

在下面的代码中,行会阻塞线程吗?如果它阻塞线程(这是我的想法),是否有标准的不阻塞解决方案?我正在考虑使用AsyncLock,但首先我想尝试一些标准的东西。

谢谢。

private object myLock = new object(); 

private async Task MyMethod1(){
  lock (myLock ) {// <---- will this line cause a return of the current method as an Await method call would do if myLock was already locked. 

    .... 
  }
}

// other methods that lock on myLock

【问题讨论】:

标签: c# multithreading


【解决方案1】:

不允许这样做来阻止死锁(即开发人员伤害自己)。我发现的最佳解决方案是使用信号量 - See this post for details

相关代码摘录:

static SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1, 1);

...

await semaphoreSlim.WaitAsync();
try
{
    await Task.Delay(1000);
}
finally
{
    semaphoreSlim.Release();
}

【讨论】:

  • 将信号量设为静态是否有原因?如果信号量是一个实例变量(假设你只想锁定这个类的实例),它可能会同样有效
  • 就我而言,我有多个对象需要向数据库报告其进度。我当然希望它们是异步的,所以在我的情况下使用静态 SemaphoreSlim 是可行的方法。
【解决方案2】:

不会的。

lockMonitor.EnterMonitor.Exit 的语法糖。 lock 将在方法中继续执行,直到锁被释放。它在任何方面、形状或形式上都不像await

【讨论】:

  • 谢谢。顺便说一句:我想适用于我的问题:“是的,线程会阻塞”,是我正在寻找的答案。
  • lock() 旋转一小段时间然后阻塞。如果它只是不停地旋转,它会将低效的代码变成低效的耗时耗电的代码。
  • 我对“旋转”这个词的使用不当。我只是想说执行不会像await 那样离开方法。我会改写。谢谢@CoryNelson。
【解决方案3】:

在下面的代码中,行会阻塞线程吗?

从技术上讲,是的,但它不会像你期望的那样工作。

线程仿射锁不能很好地与async 配合使用有两个原因。一个是(在一般情况下),async 方法可能不会在同一个线程上恢复,因此它会尝试释放它不拥有的锁,而另一个线程永远持有锁。另一个原因是在持有锁的await 期间,任意代码可能会在持有锁的同时执行。

因此,编译器会竭尽全力禁止 await 块内的 await 表达式。不过,您仍然可以直接使用Monitor 或其他原语来打自己的脚。

如果它阻塞线程(这是我的想法),是否有标准的不阻塞解决方案?

是的; SemaphoreSlim 类型支持WaitAsync

【讨论】:

  • 感谢我在lock 中看到await 的问题。相反的情况如何:在最终被某个异步方法调用的方法中使用locks?我的猜测是它是安全的,因为locked 操作都是在同一个线程上完成的。但是,在这种情况下,直接使用 Monitors 似乎仍然存在问题 - 但这很糟糕,因为开发人员通常无法判断代码是否将以 async/await 方式使用。有什么建议?
  • 只要锁(或监视器)中只有同步代码,就可以正常工作。是否被异步方法调用都没有关系。
  • 我认为这就是问题所在(异步方法中的锁是否安全)。谢天谢地,我意识到有点不清楚并向下滚动到这些 cmets。哈哈哈。
  • @OfirD:有一个主要问题:async 方法可能会在 await 之后的不同线程上恢复执行,从而导致线程释放锁定它从未获得过。
  • @OfirD:现在考虑在 same 线程 1 上运行的多个异步方法:函数 A 获取锁并执行 await。然后函数 B 在同一个线程上运行;如果它获得相同的锁,它将成功而不是阻塞。此外,在更一般的情况下,函数 B 可以依赖于获取锁的线程 2 运行的某些代码 - 在这种情况下,您最终会出现死锁,因为线程 1 持有锁,等待线程 2,而线程 2 正在等待线程 1 释放锁。
【解决方案4】:

您的任务不会返回挂起状态。它将等到 myLock 被解锁以在 lock 语句中运行代码。无论您使用什么 C# 异步模型,它都会发生。

换句话说,没有两个线程能够在锁内运行语句。除非,有许多不同的 myLock 对象实例。

【讨论】:

    猜你喜欢
    • 2022-12-17
    • 2021-09-03
    • 2017-12-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-12
    • 1970-01-01
    相关资源
    最近更新 更多