当前位置: 首页 > 知识库问答 >
问题:

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

东方智敏
2023-03-14

我写了一个多线程应用程序,它广泛使用了async/await。它应该在预定的时间下载一些东西。为此,它使用了“await 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!");

我必须在每个await和task.run前后以及每个异步函数内部使用它,这既难看又不方便。我不能将它放入一个单独函数中,因为它必须是异步的,而且无论如何我都必须等待它。有没有人想出一个更好的解决方案?

编辑:

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

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

我有一个有四个核心的处理器,通过从 中减去 的结果,我得到0个繁忙的工作线程和1-2个繁忙的完成端口线程。 通常返回大约30。这是一个Windows窗体应用程序,虽然它只有一个带菜单的托盘图标,但它一开始就有11个线程。当它达到每分钟发送数千个请求时,它很快就会达到30个。

正如@noseratio建议的那样,我试着玩 ,但它甚至没有改变上面提到的繁忙线程的数量。

共有3个答案

公孙涵育
2023-03-14

我认为您正在经历Joe Duffy在他的“CLR线程池注入,口吃问题”博文中描述的情况:

线程池目前做的一件傻事是如何创建新线程。也就是说,一旦超过最小线程数(默认情况下是计算机上的CPU数),它将严重限制新线程的创建。一旦达到或超过这个数字,我们将自己限制在每500ms最多一个新线程。

null

ThreadPool.SetMaxThreads(workerThreads: 200, completionPortThreads: 200);
ThreadPool.SetMinThreads(workerThreads: 100, completionPortThreads: 100);

null

东郭鹤龄
2023-03-14

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);
}

调节睡眠(。。)和间隔值。

钱星华
2023-03-14

当您执行task.run时,它使用线程池线程来执行这些任务。当您有长时间运行的任务时,您会导致线程池的饥饿,因为它的资源当前被长时间运行的任务占用。

2个建议:

> 应用程序变慢>

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

 类似资料:
  • 在C#中,我有以下两个简单的例子: 第一个示例创建一个打印“开始”的任务,等待5秒钟打印“完成”,然后结束任务。我等待任务完成,然后打印“全部完成”。当我运行测试时,它会按预期运行。 第二个测试应该具有相同的行为,只是由于使用了async和Wait,任务内部的等待应该是非阻塞的。但是这个测试只打印“开始”,然后立即打印“全部完成”和“完成”,永远不会打印。 我不知道我为什么会有这样的行为:S非常感

  • 问题内容: 我如何将while循环延迟到1秒的间隔,而又不将正在运行的整个代码/计算机的速度减慢到1秒钟的延迟(只是一个小循环)。 问题答案:

  • 我在网上搜索了很多关于vs await async,但是在这个特定的使用场景中,我并不真正理解其中的区别。我相信情况很简单。 vs. 其中,是一个异步方法,其中包含一些异步调用,例如使用wait调用db。 问题: 在这种情况下,两者之间有什么区别吗?任何帮助或意见,谢谢!

  • 问题内容: 我想知道一段时间后如何调用函数。我已经尝试过time.sleep(),但是这会暂停整个脚本。我希望脚本继续进行,但是??? secs之后调用一个函数并同时运行其他脚本 问题答案: 看一看。它在新线程中运行您的函数。

  • 我试图对其运行黑盒测试的web应用程序可以使用ajax调用,但这些ajax调用在DOM上不呈现任何内容,因此我不能使用显式等待。此外,隐式只适用于语句,同样没有用处。 我成功地使用了,但我希望有更好的方法来延迟执行。

  • 使用asyn/wait vs wait有什么区别task.run() 等待任务。运行示例- 异步等待示例-