简体   繁体   English

异步非轮询处理机制等到所有方法调用完成 .NET 5 C#

[英]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> )方法MDisposeCoreAsync 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM