Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/287.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 等待任务。延迟时间比预期的长_C#_.net_Asynchronous_Task Parallel Library_Async Await - Fatal编程技术网

C# 等待任务。延迟时间比预期的长

C# 等待任务。延迟时间比预期的长,c#,.net,asynchronous,task-parallel-library,async-await,C#,.net,Asynchronous,Task Parallel Library,Async Await,我编写了一个多线程应用程序,广泛使用async/await。它应该在预定的时间下载一些东西。为了实现这一点,它使用“wait Task.Delay”。有时它每分钟发送数千个请求 它按预期工作,但有时我的程序需要记录一些大的日志。当它这样做时,它会序列化许多对象并将它们保存到一个文件中。在这段时间里,我注意到我安排的任务执行得太晚了。我已经将所有日志记录放在一个具有最低优先级的单独线程中,这个问题不再经常发生,但它仍然会发生。问题是,我想知道它何时发生,为了知道我必须使用这样的东西: var de

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

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

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项建议:

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

  • 在必须执行IO绑定工作的地方使用true async,使用支持TAP的API,如HttpClient和Stream,这不会导致新线程执行阻塞工作


  • 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个线程在旋转?为什么这么多?这真的很有道理,你会觉得你的任务开始“晚”,因为大规模的欺诈