等待Task.Delay花费比预期更长的时间 [英] await Task.Delay takes longer than expected

查看:345
本文介绍了等待Task.Delay花费比预期更长的时间的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我编写了一个多线程应用程序,该应用程序广泛使用async/await.它应该在预定的时间下载一些东西.为此,它使用"await Task.Delay".有时,它每分钟发送数千个请求.

I wrote a multithreaded app which uses async/await extensively. It is supposed to download some stuff at a scheduled time. To achieve that, it uses 'await Task.Delay'. Sometimes it sends thousands requests every minute.

它可以按预期工作,但是有时我的程序需要记录一些大内容.这样做时,它将序列化许多对象并将其保存到文件中.在那段时间里,我注意到我的计划任务执行得太迟了.我已将所有日志记录放到具有最低优先级的单独线程中,该问题不再经常发生,但仍然会发生.问题是,我想知道它何时发生,并且想知道我必须使用类似的东西:

It works as expected, but sometimes my program needs to log something big. When it does, it serializes many objects and saves them to a file. During that time, I noticed that my scheduled tasks are executed too late. I've put all the logging to a separate thread with the lowest priority and the problem doesn't occur that often anymore, but it still happens. The things is, I want to know when it happens and in order to know that I have to use something like that:

var delayTestDate = DateTime.Now;
await Task.Delay(5000);
if((DateTime.Now - delayTestDate).TotalMilliseconds > 6000/*delays up to 1 second are tolerated*/) Console.WriteLine("The task has been delayed!");

此外,我发现我也使用的'Task.Run'也可能导致延迟.要监视它,我必须使用更难看的代码:

Moreover, I have found that 'Task.Run', which I also use, can also cause delays. To monitor that, I have to use even more ugly code:

var delayTestDate = DateTime.Now;
await Task.Run(() =>
{
  if((DateTime.Now - delayTestDate).TotalMilliseconds > 1000/*delays up to 1 second are tolerated*/) Console.WriteLine("The task has been delayed!");
  //do some stuff
  delayTestDate = DateTime.Now;
});
if((DateTime.Now - delayTestDate).TotalMilliseconds > 1000/*delays up to 1 second are tolerated*/) Console.WriteLine("The task has been delayed!");

我必须在每次await和Task.Run之前和之后和每个异步函数内部使用它,这很丑陋且不方便.我不能将其放在单独的函数中,因为它必须是异步的,而且无论如何我都必须等待它.有人有一个更优雅的解决方案的想法吗?

I have to use it before and after every await and Task.Run and inside every async function, which is ugly and inconvenient. I can't put it into a separate function, since it would have to be async and I would have to await it anyway. Does anybody have an idea of a more elegant solution?

我在评论中提供的一些信息:

Some information I provided in the comments:

正如@YuvalItzchakov所注意到的,该问题可能是由线程池饥饿引起的.这就是为什么我使用System.Threading.Thread来处理线程池之外的日志记录的原因,但是正如我所说的,问题有时仍然会发生.

As @YuvalItzchakov noticed, the problem may be caused by Thread Pool starvation. That's why I used System.Threading.Thread to take care of the logging outside of the Thread Pool, but as I said, the problem still sometimes occur.

我有一个具有四个内核的处理器,通过从ThreadPool.GetMaxThreads中减去ThreadPool.GetAvailableThreads的结果,我得到0个繁忙的工作线程和1-2个繁忙的完成端口线程. Process.GetCurrentProcess().Threads.Count通常返回大约30.这是Windows Forms应用程序,尽管它只有一个带有菜单的任务栏图标,但它以11个线程开头.每分钟要发送数千个请求时,它很快就会达到30个.

I have a processor with four cores and by subtracting results of ThreadPool.GetAvailableThreads from ThreadPool.GetMaxThreads I get 0 busy worker threads and 1-2 busy completion port threads. Process.GetCurrentProcess().Threads.Count usually returns about 30. It's a Windows Forms app and although it only has a tray icon with a menu, it starts with 11 threads. When it gets to sending thousands requests per minute, it quickly gets up to 30.

按照@Noseratio的建议,我尝试使用ThreadPool.SetMinThreadsThreadPool.SetMaxThreads,但是它甚至都没有改变上述繁忙线程的数量.

As @Noseratio suggested, I tried to play with ThreadPool.SetMinThreads and ThreadPool.SetMaxThreads, but it didn't even change the numbers of busy threads mentioned above.

推荐答案

执行Task.Run时,它使用线程池线程来执行那些任务.当您有长时间运行的任务时,由于线程资源当前被长时间运行的任务占用,因此会导致线程池饥饿.

When you execute Task.Run it uses Thread Pool threads to execute those tasks. When you have long running tasks, you are causing starvation to the Thread Pool, since its resources are currently occupied with long running tasks.

2条建议:

  1. 在运行长时间运行的任务时,请确保将Task.Factory.Startnew与TaskCreationOptions.LongRunning一起使用,这将触发新线程的创建.您在这里也必须谨慎,因为旋转太多新线程将导致过多的上下文切换,这将导致您的应用程序运行缓慢

  1. When running long running tasks, make sure to use Task.Factory.Startnew with TaskCreationOptions.LongRunning, which will trigger a new thread creation. You must be cautious here as well, as spinning too many new threads will cause excessive context switches which will cause your app to slow down

在需要执行IO绑定工作的地方使用真正的异步,使用支持TAP的api(例如HttpClient和Stream),它们不会导致新线程执行阻止工作.

Use true async where you have to do IO Bound work, use apis that support the TAP such as HttpClient and Stream, which wont cause a new thread to execute blocking work.

这篇关于等待Task.Delay花费比预期更长的时间的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆