C# 等待任务。延迟时间比预期的长
我编写了一个多线程应用程序,广泛使用async/await。它应该在预定的时间下载一些东西。为了实现这一点,它使用“wait Task.Delay”。有时它每分钟发送数千个请求 它按预期工作,但有时我的程序需要记录一些大的日志。当它这样做时,它会序列化许多对象并将它们保存到一个文件中。在这段时间里,我注意到我安排的任务执行得太晚了。我已经将所有日志记录放在一个具有最低优先级的单独线程中,这个问题不再经常发生,但它仍然会发生。问题是,我想知道它何时发生,为了知道我必须使用这样的东西:C# 等待任务。延迟时间比预期的长,c#,.net,asynchronous,task-parallel-library,async-await,C#,.net,Asynchronous,Task Parallel Library,Async Await,我编写了一个多线程应用程序,广泛使用async/await。它应该在预定的时间下载一些东西。为了实现这一点,它使用“wait Task.Delay”。有时它每分钟发送数千个请求 它按预期工作,但有时我的程序需要记录一些大的日志。当它这样做时,它会序列化许多对象并将它们保存到一个文件中。在这段时间里,我注意到我安排的任务执行得太晚了。我已经将所有日志记录放在一个具有最低优先级的单独线程中,这个问题不再经常发生,但它仍然会发生。问题是,我想知道它何时发生,为了知道我必须使用这样的东西: var de
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”也会导致延迟。为了监控这一点,我必须使用更难看的代码:
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!");
我必须在每次等待和任务之前和之后使用它。在每个异步函数内部运行它,这既丑陋又不方便。我不能把它放在一个单独的函数中,因为它必须是异步的,我无论如何都要等待它。有人有更优雅的解决方案吗
编辑:
我在评论中提供的一些信息:
正如@YuvalItzchakov所注意到的,这个问题可能是由线程池不足引起的。这就是为什么我使用System.Threading.Thread
来处理线程池之外的日志记录,但是正如我所说的,这个问题有时仍然会发生
我有一个具有四个内核的处理器,通过从ThreadPool.GetAvailableThreads
中减去ThreadPool.GetMaxThreads
的结果,我得到0个繁忙的工作线程和1-2个繁忙的完成端口线程Process.GetCurrentProcess().Threads.Count
通常返回大约30。这是一个Windows窗体应用程序,虽然它只有一个带有菜单的托盘图标,但它从11个线程开始。当它达到每分钟发送数千个请求时,它很快就会达到30个
正如@nosertio所建议的,我尝试使用
ThreadPool.SetMinThreads
和ThreadPool.SetMaxThreads
,但它甚至没有改变上面提到的繁忙线程数。当您执行任务时。Run它使用线程池线程来执行这些任务。当您有长时间运行的任务时,会导致线程池饥饿,因为其资源当前被长时间运行的任务占用
2项建议:
async/await中存在开销,任务本身也以较低的优先级执行。如果您需要以准确的时间间隔可靠地发生某些事情,那么async/await/TPL不是要使用的接口 尝试创建一个独立的后台线程,该线程将循环,直到计划好执行任务为止。通过这种方式,您可以直接控制优先级和计时,而无需通过TPL/async
Thread backgroundThread = new Thread(BackgroundWork);
DateTime nextInterval = DateTime.Now;
public void BackgroundWork()
{
if(DateTime.Now > nextInterval){
DoWork();
nextInterval = nextInterval.Add(new TimeSpan(0,0,0,10)); // 10 seconds
}
Thread.Sleep(100);
}
根据需要调整睡眠(..)和间隔值。我想你正在经历乔·达菲在他的博客文章中描述的情况: 我们的线程池目前所做的一件愚蠢的事情与它的工作方式有关 创建新线程。也就是说,它严重阻碍了新技术的产生 线程数一旦超过“最小”线程数,即 默认值是计算机上的CPU数量。我们只限于 一旦我们达到或超过这个数字,每500ms最多有一条新螺纹 一种解决方案可能是在使用TPL之前显式增加线程池线程的最小数量,例如:
ThreadPool.SetMaxThreads(workerThreads: 200, completionPortThreads: 200);
ThreadPool.SetMinThreads(workerThreads: 100, completionPortThreads: 100);
尝试使用这些数字,看看问题是否消失 尽管运行
Thread.Sleep
会阻止整个线程在执行Task时不执行任何操作,但延迟
的可伸缩性要高得多。@nikeee但这种可伸缩性也有成本,这正是这个问题的核心所在。我不太喜欢这个解决方案,因为我真的很喜欢使用async/Wait/TPL。编写代码更容易,许多.NET Framework对象都支持它。但如果没有其他问题,我会把它作为一个答案。谢谢。-1与使用TPL、async相比没有什么优势。建议改为使用Wait Task.Wait(100)。configurewait(false)
。显然会产生大量开销。仅具有任务延迟(1)代码>(其他什么都没有)在超过1000次迭代的for循环中,我需要14秒,而不是1秒。为什么您需要知道每个延迟?你不可以看看其他指标吗?@svick有些活动安排在特定的时间,我需要监控。所以不,时间是我唯一的参考点。我也面临同样的问题。我知道线程池不足。让我吃惊的是,当我在一个独立的线程中运行日志记录作业时,它仍然会发生,而这个线程池并不控制它(我只是使用System.Threading.thread)。另外,“ThreadPool.GetAvailableThreads”显示只有一个线程正忙。至于你的其他建议,任务运行时间不长,我在任何地方都使用真正的异步。你运行代码的机器有多少内核?四个内核Process.GetCurrentProcess().Threads.Count
通常返回大约30个线程,更准确地说,ThreadPool.GetAvailableThreads
返回0个繁忙的工作线程和1-2个繁忙的完成端口线程,这有点奇怪。所以在4个内核上同时有大约35个线程在旋转?为什么这么多?这真的很有道理,你会觉得你的任务开始“晚”,因为大规模的欺诈