我经常发现我的组件具有公共异步(返回Task<T>
)方法M
和DisposeCoreAsync
。其他组件通过调用M
来使用此组件。应用程序运行,并在某个时候调用关闭。停机是对所有组件的级联处置。假设执行M
可能需要很长时间。那么问题就出现了,当调用DisposeCoreAsync
时,一个或多个线程正在执行M
并且位于其中的某个位置。我想找到一个原始(或简单机制)P
,优先使用纯 .NET 5 库,这样我就可以写这样的东西:
public async Task<bool> M()
{
if (!P.Increment())
return false; // Shutdown detected
...
P.Decrement(); // Only after this happens for all active executions, disposal can finish
return result;
}
protected virtual async ValueTask DisposeCoreAsync()
{
...
P.Shutdown(); // This should prevent new P.Increment to succeed
await P.WaitAllFinishedAsync().ConfigureAwait(false);
// From here down we are guaranteed that no thread will ever execute the actual body of M().
...
}
因此,P.Increment
应该增加一个主动处决M身体的计数器。P.Decrement
递减计数器。P.Shutdown
确保任何进一步的P.Increment
尝试都失败。P.WaitAllFinishedAsync
等到 P 的计数器变为 0。
如果我允许忙于等待,这一切都是微不足道的。我可以像Interlocked.Increment
一样实现P.Increment
,我可以像Interlocked.Decrement
一样实现P.Decrement
,我可以用一些非常高的位实现Interlocked.Decrement
P.Shutdown
,比如0x1000000。只有当递增新值为正时,P.Increment
才会成功。然后我可以P.WaitAllFinishedAsync
实现为while (P.counter > -0x1000000) Task.Delay(10);
但是,如何在不定期检查我们是否在那里的情况下正确地做到这一点WaitAllFinishedAsync
呢?此外,如何以最小的开销执行M
来做到这一点?它只是关闭机制,因此不应给频繁执行M
带来沉重负担。
当然,我可能会使用using
并在P.Dispose
内部而不是P.Increment/Decrement
中制作P.Decrement
,以确保内部的任何异常或返回都不会忘记"释放"P
,但这只是一个实现细节。
您要查找的基元是异步兼容的倒计时事件。BCL 中没有执行此操作的类型,但您可以自己生成一个类型。我这里有一个开源的,你可以用它来获得灵感。