在异步环境中共享资源池的有效方法是什么?

dro*_*owa 3 .net c# asynchronous

想象一下有几个任务试图同时使用资源池。池中的单个资源一次只能由特定数量的任务使用;这个数字可以是一个。

同步环境中,在我看来WaitHandle.WaitAnySemaphore是必经之路。

var resources = new[] { new Resource(...), new Resource(...) }; // 'Resource' custom class wrapers the resource
var semaphores = new[] { new Semaphore(1, 1), new Semaphore(1, 1) };
... 
var index = WaitHandle.WaitAny(semaphores);

try
{
    UseResource(resources[index]);
}
finally
{
    semaphores[index].Release();
}
Run Code Online (Sandbox Code Playgroud)

但是我们应该在异步环境中做什么?

Ste*_*ary 5

我通常建议开发人员将“池”逻辑与“使用”逻辑分开。这种分离的一个不错的好处是,只有池逻辑需要同步。

在实际情况下,资源数量是运行时已知的;更确切地说,它将在应用程序初始化(即配置)时进行。

服务器上的每个端口一次只能接受一个客户端,并且每个服务器只有一个可用端口。

因此,您有一组有限的资源,并且每个资源一次只能由一个线程使用。

由于您无法按需创建新资源,因此需要一个信号来知道何时可用。您可以自己执行此操作,也可以使用诸如a之类的内容BufferBlock<T>作为异步就绪队列。

由于每个资源一次只能由一个线程使用,因此我建议使用通用IDisposable技术将资源释放回池中。

将这些放在一起:

public sealed class Pool
{
  private readonly BufferBlock<Resource> _block = new BufferBlock<Resource>();

  public Pool()
  {
    _block.Post(new Resource(this, ...));
    _block.Post(new Resource(this, ...));
  }

  public Resource Allocate()
  {
    return _block.Receive();
  }

  public Task<Resource> AllocateAsync()
  {
    return _block.ReceiveAsync();
  }

  private void Release(Resource resource)
  {
    _block.Post(resource);
  }

  public sealed class Resource : IDisposable
  {
    private readonly Pool _pool;
    public Resource(Pool pool, ...)
    {
      _pool = pool;
      ...
    }

    public void Dispose()
    {
      _pool.Release(this);
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

用法:

using (var resource = Pool.Allocate())
    UseResource(resource);
Run Code Online (Sandbox Code Playgroud)

要么:

using (var resource = await Pool.AllocateAsync())
    await UseResourceAsync(resource);
Run Code Online (Sandbox Code Playgroud)