[英]Async non-polling disposal mechanism to wait until all method calls are finished in .NET 5 C#
Often I find my component having a public async (returning Task<T>
) method M
and DisposeCoreAsync
.我经常发现我的组件具有公共异步(返回Task<T>
)方法M
和DisposeCoreAsync
。 Other components are using this component by calling M
.其他组件通过调用M
来使用这个组件。 The application runs and at some point the shutdown is invoked.应用程序运行并在某些时候调用关闭。 The shutdown is a cascade of disposals on all components.关闭是对所有组件的一连串处置。 Assume execution of M
can take a long time.假设M
的执行可能需要很长时间。 Then the problem arises that when DisposeCoreAsync
is called, one or more threads are executing M
and are somewhere in the middle of it.那么问题就来了,当DisposeCoreAsync
被调用时,一个或多个线程正在执行M
并且在它中间的某个地方。 I'd like to find a primitive (or a simple-mechanism) P
, preferentially using pure .NET 5 libraries, such that I can write something like this:我想找到一个原始(或简单机制) 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().
...
}
So P.Increment
should increment a counter of active executions of M's body.所以P.Increment
应该增加 M 主体的主动执行的计数器。 P.Decrement
decrements the counter. P.Decrement
递减计数器。 P.Shutdown
makes sure that any further P.Increment
attempts fail. P.Shutdown
确保任何进一步P.Increment
尝试都失败。 P.WaitAllFinishedAsync
waits until the counter of P goes to 0. P.WaitAllFinishedAsync
一直等到 P 的计数器变为 0。
This all is trivial if I allow busy waiting.如果我允许忙等待,这一切都是微不足道的。 I can implement P.Increment
as Interlocked.Increment
, I can implement P.Decrement
as Interlocked.Decrement
, I can implement P.Shutdown
as Interlocked.Decrement
with some very high bit, such as 0x1000000.我可以将P.Increment
实现为Interlocked.Increment
,我可以将P.Decrement
实现为Interlocked.Decrement
,我可以将P.Shutdown
实现为Interlocked.Decrement
具有一些非常高的位,例如 0x1000000。 And P.Increment
succeeds only if after incrementing the new value is positive.并且P.Increment
只有在增加新值之后才成功。 Then I can implement P.WaitAllFinishedAsync
as while (P.counter > -0x1000000) Task.Delay(10);
然后我可以将P.WaitAllFinishedAsync
实现为while (P.counter > -0x1000000) Task.Delay(10);
But how to do it properly without WaitAllFinishedAsync
being a loop periodically checking if we are there yet?但是如果没有WaitAllFinishedAsync
是一个定期检查我们是否在那里的循环,如何正确地做到这一点呢? Moreover, how to do it with minimal overhead on execution of M
?此外,如何在执行M
时以最小的开销来做到这一点? It is only shutdown mechanism, so it should not put heavy burden on frequent executions of M
.它只是关闭机制,所以它不应该给频繁执行M
带来沉重的负担。
Of course, I'd probably use using
and make P.Decrement
inside of P.Dispose
instead of P.Increment/Decrement
, to make sure that any exceptions or returns from inside won't forget "freeing" P
, but that's just an implementation detail.当然,我可能会在P.Dispose
中使用using
和 make P.Decrement
而不是P.Increment/Decrement
,以确保任何异常或来自内部的返回都不会忘记“释放” P
,但这只是一个实施细节。
The primitive you're looking for is an async-compatible countdown event .您正在寻找的原语是异步兼容的倒计时事件。 There is no type in the BCL that does this, but you can build one yourself. BCL 中没有执行此操作的类型,但您可以自己构建一个。 I have an open-source one here you can use for inspiration.我在这里有一个开源的,你可以用它来获得灵感。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.