想象一下尝试同时使用资源池的几个任务。池中的单个资源只能由特定数量的任务使用;数字可以是一个。
在同步环境中,在我看来WaitHandle.WaitAny
& Semaphore
是可行的方法。
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();
}
但是我们应该在异步环境中做些什么呢?
答案 0 :(得分:5)
我通常建议开发人员将“池”逻辑与“使用”逻辑分开。这种分离的一个好处是,只有池化逻辑需要同步。
在真实场景中,资源的数量将是运行时已知的;更准确地说,它将在应用程序的初始化(即配置)。
服务器上的每个端口一次只接受一个客户端,每个服务器只有一个端口可用。
因此,您拥有一组有限的资源,每个资源一次只能由一个线程使用。
由于您无法按需创建新资源,因此您需要一个信号来了解何时可用。您可以自己执行此操作,也可以使用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);
}
}
}
用法:
using (var resource = Pool.Allocate())
UseResource(resource);
或:
using (var resource = await Pool.AllocateAsync())
await UseResourceAsync(resource);
答案 1 :(得分:2)
还有任何疑虑吗?发表评论。
答案 2 :(得分:0)
答案 3 :(得分:0)
遵循异步版本Task.WhenAny
& SemaphoreSlim
。
var resources = new[] { new Resource(...), new Resource(...) }; // 'Resource' custom class wrapers the resource
var semaphores = new[] { new SemaphoreSlim(1, 1), new SemaphoreSlim(1, 1) };
...
var waits = new[] { semaphores[0].WaitAsync(), semaphores[1].WaitAsync() };
var index = Array.IndexOf(waits, await Task.WhenAny(waits));
// The wait is still running - perform compensation.
if (index == 0)
waits[1].ContinueWith(_ => semaphores[1].Release());
else if (index == 1)
waits[0].ContinueWith(_ => semaphores[0].Release());
try
{
await UseResourceAsync(resources[index]);
}
finally
{
semaphores[index].Release();
}