【发布时间】:2020-07-30 13:11:55
【问题描述】:
我们正在使用看门狗来确定连接的系统是否仍然存在。
在前面的代码中,我们直接使用 TCP 并在单独的线程中处理看门狗。现在是使用 gRPC 提供数据的新服务。
为此,我们尝试对任务使用异步接口,但基于任务的看门狗会失败。
我写了一个小的DEMO 来抽象代码并说明问题。您可以通过使用 // 注释掉第 18 行来在基于任务的看门狗和基于线程的看门狗之间切换。
演示包含导致问题的代码:
async Task gRPCSendAsync(CancellationToken cancellationToken = default) => await Task.Yield();
async Task gRPCReceiveAsync(CancellationToken cancellationToken = default) => await Task.Yield();
var start = DateTime.UtcNow;
await gRPCSendAsync(cancellationToken).ConfigureAwait(false);
await gRPCReceiveAsync(cancellationToken).ConfigureAwait(false);
var end = DateTime.UtcNow;
if ((end - start).TotalMilliseconds >= 100)
// signal failing
如果在Task.Run 中使用此代码,如果应用程序在其他任务中有大量 cpu 工作要做,它将发出失败信号。
如果使用专用线程,看门狗会按预期工作,不会引发任何问题。
我确实理解这个问题:等待之后的所有代码都可能(如果尚未完成或不包含“真正的”等待)排队到线程池。但是线程池还有其他事情要做,所以完成方法的时间太长了。
是的,简单的答案是:使用线程。
但是使用线程限制了我们只能使用同步方法。无法从线程中调用异步方法。我创建了另一个sample,它显示第一个await 之后的所有代码都将排队到线程bool,这样CallAsync().Wait() 将无法工作。 (顺便说一句。这个问题得到了更多的处理here。)
我们有很多异步代码可以在这些时间紧迫的操作中使用。
所以问题是:有没有办法使用带有 async/await 的任务来执行该操作?
也许我完全错了,创建基于任务的看门狗应该以非常不同的方式完成。
想法
我在考虑System.Threading.Timer,但是异步发送和异步接收的问题无论如何都会导致这个问题。
【问题讨论】:
-
您是否考虑过increasing the minimum number of threads 线程池按需创建,例如在程序开始时调用
ThreadPool.SetMinThreads(50, 10);? -
附带说明,比较通过调用
UtcNow获得的DateTimes 不是衡量持续时间的可靠方法,因为它取决于可能受到非确定性调整的系统时钟.我建议改用Stopwatch。 -
@TheodorZoulias 是的,我做到了——但这不是真正的解决方案。这只会将问题稍微转移到未来。此外,并非每个使用的 CPU 都提供 64 个线程,因此这将是线程切换成本和线程数之间的走钢丝。
-
@TheodorZoulias 是的,我知道。我们的生产代码使用的是秒表,但这并不重要,我尝试创建一个简单的演示来说明问题。
-
@TheodorZoulias 我们目前出于其他原因使用
Nito.AsyncEx,没有理由反对Context。我之前没有尝试过,因为我不知道它。我会试一试——在阅读了一些文档以了解它的作用之后。谢谢你的提示。
标签: c# multithreading async-await task