Task based vs. thread based Watchdog - but async needed

断了今生、忘了曾经 提交于 2021-01-28 06:13:32

问题


We're using watchdogs to determine whether a connected system is still alive or not.

In the previous code we used TCP directly and treated the watchdog in a separate thread. Now is a new service used that provides it's data using gRPC.

For that we tried using the async interface with tasks but a task based watchdog will fail.

I wrote a small DEMO that abstracts the code and illustrates the problem. You can switch between task based watchdog and thread based watchdog by commenting out line 18 with //.

The demo contains this code that causes the problem:

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

If this code is used in Task.Run it will signal failing if the application has a lot cpu-work to do in other tasks.

If a dedicated thread is used the watchdog works as expected and no problem is raise.

I do understand the problem: All code after await may be (if not finished already or does not contain a "real" await) queued to the thread pool. But the thread pool has other things to do so that it took too long to finish the method.

Yes the simple answer is: USE THREAD.

But using a thread limits us to only use synchronous methods. There is no way to call an async method out of a thread. I created another sample that shows that all code after first await will be queued to thread bool so that CallAsync().Wait() will not work. (Btw. that issue is much more handled here.)

We're having a lot of async code that may be used within such time critical operations.

So the question is: Is there any way to perform that that operations using tasks with async/await?

Maybe I'm completely wrong and creating an task based watchdog should be done very differently.

thoughts

I was thinking about System.Threading.Timer but the problem of async sending and async receiving will cause that problem anyways.


回答1:


Here is how you could use Stephen Cleary's AsyncContext class from the Nito.AsyncEx.Context package, in order to constrain an asynchronous workflow to a dedicated thread:

await Task.Factory.StartNew(() =>
{
    AsyncContext.Run(async () =>
    {
        await DoTheWatchdogAsync(watchdogCts.Token);
    });
}, TaskCreationOptions.LongRunning);

The call to AsyncContext.Run will block until the supplied asynchronous operation is completed. All asynchronous continuations created by the DoTheWatchdogAsync will be processed internally by the AsyncContext on the current thread. In the above example the current thread is not a ThreadPool thread, because of the flag TaskCreationOptions.LongRunning used in the construction of the wrapper Task. You could confirm this by querying the property Thread.CurrentThread.IsThreadPoolThread.

If you prefer you could use a traditional Thread constructor instead of the somewhat unconventional Task.Factory.StartNew+LongRunning.



来源:https://stackoverflow.com/questions/63173945/task-based-vs-thread-based-watchdog-but-async-needed

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!