为什么没有类似Monitor.EnterAsync的方法

时间:2016-03-15 18:15:57

标签: c#

我在新框架中看到许多方法,它们在C#中使用async/await的新异步模式/语言支持。为什么没有Monitor.EnterAsync()或其他async lock机制来释放当前线程& lock可用后立即返回

我认为这是不可能的 - 问题是为什么?

5 个答案:

答案 0 :(得分:4)

我想问题是,通过调用concatMap,当前的线程想获得传递对象的锁定。所以你应该问问自己如何实现Monitor.Enter?第一个天真的尝试是:

Monitor.EnterAsync

但这显然没有达到预期的效果,因为锁定将通过为新public async Task EnterAsync(object o) { await Task.Run(() => Monitor.Enter(o)); } 启动的线程而不是调用线程获得。
您现在需要一种机制来确保您可以在等待之后获得锁定。但我目前无法想出如何确保这种方法能够正常工作以及其他任何线程都无法获得锁定的方法。

这些只是我的2美分(如果它不太长就会发表评论)。我期待有更多详细知识的人为您提供更有启发性的答案。

答案 1 :(得分:4)

.Net提供的一些同步原语是底层本机对象的托管包装器。

目前,没有实现异步锁定的本机同步原语。因此.Net实现者必须从头开始实现,这并不像看起来那么简单。

此外,Windows内核不提供" locking-delegation"的任何功能,这意味着您无法锁定一个线程中的锁,并将所有权传递给另一个线程,这使得实施这种锁的工作极其困难。

在我看来,第三个原因是更具哲学性的 - 如果你不想阻止 - 使用非阻塞技术,比如使用异步IO,无锁算法和数据结构。如果您的应用程序的瓶颈是激烈的争用以及它周围的锁定开销,您可以以不同的形式重新设计应用程序,而无需异步锁定。

答案 2 :(得分:3)

虽然默认情况下.NET 中没有异步监视器,但Stephen Cleary有一个很棒的库AsyncEx,可以在使用async / await时处理同步问题。

它有一个AsyncMonitor类,它几乎完全符合您的要求。您可以从GitHubNuGet package获取。

用法示例:

var monitor = new AsyncMonitor();
using (await monitor.EnterAsync())
{
    // Critical section
}

答案 3 :(得分:3)

  

我认为这是不可能的 - 问题是为什么?

有可能,它还没有完成。

目前,BCL中唯一的异步兼容同步原语是SemaphoreSlim,它可以充当信号量或简单的互斥锁。

我有一个基本的AsyncMonitor that I wrote,基于Stephen Toub's blog post series。请注意,语义与BCL Monitor略有不同;特别是,它不允许递归锁定(对于reasons I describe on my blog)。

答案 4 :(得分:0)

这对我来说很好 https://docs.microsoft.com/en-us/dotnet/api/system.threading.semaphoreslim

信号量有两种类型:本地信号量命名系统信号量

前者是应用程序本地的。后者在整个操作系统中都是可见的,并且适合于进程间同步。

SemaphoreSlim是不使用Windows内核信号量的Semaphore类的轻量级替代。与Semaphore类不同,SemaphoreSlim类不支持命名系统信号。

您只能将其用作本地信号灯。 SemaphoreSlim类是建议在单个应用程序内进行同步的信号量。

public class ResourceLocker
{
   private Dictionary<string, SemaphoreSlim> _lockers = null;

   private object lockObj = new object();

   public ResourceLocker()
   {
      _lockers = new Dictionary<string, SemaphoreSlim>();
   }

   public SemaphoreSlim GetOrCreateLocker(string resource)
   {
       lock (lockObj)
       {
          if (!_lockers.ContainsKey(resource))
          {
             _lockers.Add(resource, new SemaphoreSlim(1, 1));
          }

             return _lockers?[resource];
        }
    }

    public bool ReleaseLocker(string resource)
    {
       lock (lockObj)
       {
         if (_lockers.ContainsKey(resource))
         {
           var locker = _lockers?[resource];

           if (locker != null)
           {
             locker.Release();

             return true;
            }

             _lockers.Remove(resource);
          }
          return false;
        }//lock
      }
 }

用法

var resource = "customResource";
var someObject = new SomeObject();
SomeResponse response = null;
var resourceLocker = new ResourceLocker();
try
  {
    var semaSlim = resourceLocker.GetOrCreateLocker(resource);

    semaSlim.Wait();     

    response = someObject.DoSomething();
   }
   finally
   {
     resourceLocker.ReleaseLocker(resource);
   }     

异步

   Task.Run(async ()=>{
        var semaSlim = resourceLocker.GetOrCreateLocker(resource);

        await semaSlim.WaitAsync();     

        response = someObject.DoSomething();

        resourceLocker.ReleaseLocker(resource);
    });