在 .NET 5 C# 中等待所有方法调用完成的异步非轮询处理机制
Async non-polling disposal mechanism to wait until all method calls are finished in .NET 5 C#
我经常发现我的组件有一个 public 异步(返回 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。
如果让我忙着等待,这一切都是微不足道的。我可以将 P.Increment
实现为 Interlocked.Increment
,我可以将 P.Decrement
实现为 Interlocked.Decrement
,我可以将 P.Shutdown
实现为 Interlocked.Decrement
,并且具有非常高的位,例如 0x1000000。并且 P.Increment
只有在递增新值后为正时才会成功。然后我可以将 P.WaitAllFinishedAsync
实现为 while (P.counter > -0x1000000) Task.Delay(10);
但是如何正确地做到这一点而不 WaitAllFinishedAsync
是一个周期性地检查我们是否已经到达的循环?此外,如何以最小的开销执行 M
?它只是关闭机制,因此不应该对 M
.
的频繁执行造成沉重负担
当然,我可能会使用 using
并在 P.Dispose
中创建 P.Decrement
而不是 P.Increment/Decrement
,以确保任何异常或 returns 从内部不会忘记“释放”P
,但这只是一个实现细节。
您正在寻找的原语是异步兼容的countdown event. There is no type in the BCL that does this, but you can build one yourself. I have an open-source one here,您可以从中获取灵感。
我经常发现我的组件有一个 public 异步(返回 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。
如果让我忙着等待,这一切都是微不足道的。我可以将 P.Increment
实现为 Interlocked.Increment
,我可以将 P.Decrement
实现为 Interlocked.Decrement
,我可以将 P.Shutdown
实现为 Interlocked.Decrement
,并且具有非常高的位,例如 0x1000000。并且 P.Increment
只有在递增新值后为正时才会成功。然后我可以将 P.WaitAllFinishedAsync
实现为 while (P.counter > -0x1000000) Task.Delay(10);
但是如何正确地做到这一点而不 WaitAllFinishedAsync
是一个周期性地检查我们是否已经到达的循环?此外,如何以最小的开销执行 M
?它只是关闭机制,因此不应该对 M
.
当然,我可能会使用 using
并在 P.Dispose
中创建 P.Decrement
而不是 P.Increment/Decrement
,以确保任何异常或 returns 从内部不会忘记“释放”P
,但这只是一个实现细节。
您正在寻找的原语是异步兼容的countdown event. There is no type in the BCL that does this, but you can build one yourself. I have an open-source one here,您可以从中获取灵感。