Task.Delay().Wait()发生了什么?

编程入门 行业动态 更新时间:2024-10-25 00:25:00
本文介绍了Task.Delay().Wait()发生了什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述

我很困惑为什么Task.Delay().Wait()要花费 4倍的时间,然后是Thread.Sleep()?

I'm confused why Task.Delay().Wait() takes 4x more time, then Thread.Sleep()?

例如 task-00 在仅线程9 上运行,并花费了 2193ms ? 我知道,同步等待在任务中是不好的,因为整个线程都被阻塞了.只是为了测试.

E.g. task-00 was running on only thread 9 and took 2193ms? I'm aware, that sync wait is bad in tasks, because the whole thread being blocked. It is just for test.

在控制台应用程序中进行简单测试:

Simple test in console application:

bool flag = true; var sw = Stopwatch.StartNew(); for (int i = 0; i < 10; i++) { var cntr = i; { var start = sw.ElapsedMilliseconds; var wait = flag ? 100 : 300; flag = !flag; Task.Run(() => { Console.WriteLine($"task-{cntr.ToString("00")} \t ThrID: {Thread.CurrentThread.ManagedThreadId.ToString("00")},\t Wait={wait}ms, \t START: {start}ms"); //Thread.Sleep(wait); Task.Delay(wait).Wait(); Console.WriteLine($"task-{cntr.ToString("00")} \t ThrID: {Thread.CurrentThread.ManagedThreadId.ToString("00")},\t Wait={wait}ms, \t END: {sw.ElapsedMilliseconds}ms"); ; }); } } Console.ReadKey(); return;

使用Task.Delay().Wait(): task-03 ThrID:05,Wait = 300ms,START:184ms 任务04阈值ID:07,等待= 100毫秒,开始:184毫秒 task-00 ThrID:09,Wait = 100ms,START:0ms 任务06 ThrID:04,等待= 100毫秒,开始:185毫秒 task-01 ThrID:08,Wait = 300ms,START:183ms 任务05阈值ID:03,等待= 300ms,开始:185ms 任务02 ThrID:06,等待= 100毫秒,开始:184毫秒 task-07 ThrID:10,Wait = 300ms,START:209ms 任务07 ThrID:10,等待= 300ms,END:1189ms task-08 ThrID:12,等待时间= 100毫秒,START:226毫秒 任务09阈值ID:10,等待= 300ms,开始:226ms 任务09阈值ID:10,等待= 300ms,结束:2192ms task-06 ThrID:04,Wait = 100ms,END:2193ms task-08 ThrID:12,Wait = 100ms,END:2194ms 任务05阈值ID:03,等待= 300ms,结束:2193ms task-03 ThrID:05,Wait = 300ms,END:2193ms task-00 ThrID:09,Wait = 100ms,END:2193ms task-02 ThrID:06,等待= 100ms,END:2193ms 任务04阈值ID:07,等待= 100ms,结束:2193ms task-01 ThrID:08,Wait = 300ms,END:2193ms

With Task.Delay().Wait(): task-03 ThrID: 05, Wait=300ms, START: 184ms task-04 ThrID: 07, Wait=100ms, START: 184ms task-00 ThrID: 09, Wait=100ms, START: 0ms task-06 ThrID: 04, Wait=100ms, START: 185ms task-01 ThrID: 08, Wait=300ms, START: 183ms task-05 ThrID: 03, Wait=300ms, START: 185ms task-02 ThrID: 06, Wait=100ms, START: 184ms task-07 ThrID: 10, Wait=300ms, START: 209ms task-07 ThrID: 10, Wait=300ms, END: 1189ms task-08 ThrID: 12, Wait=100ms, START: 226ms task-09 ThrID: 10, Wait=300ms, START: 226ms task-09 ThrID: 10, Wait=300ms, END: 2192ms task-06 ThrID: 04, Wait=100ms, END: 2193ms task-08 ThrID: 12, Wait=100ms, END: 2194ms task-05 ThrID: 03, Wait=300ms, END: 2193ms task-03 ThrID: 05, Wait=300ms, END: 2193ms task-00 ThrID: 09, Wait=100ms, END: 2193ms task-02 ThrID: 06, Wait=100ms, END: 2193ms task-04 ThrID: 07, Wait=100ms, END: 2193ms task-01 ThrID: 08, Wait=300ms, END: 2193ms

使用Thread.Sleep(): task-00 ThrID:03,Wait = 100ms,START:0ms 任务03 ThrID:09,等待= 300ms,开始:179ms 任务02 ThrID:06,等待= 100ms,开始:178ms 任务04阈值ID:08,等待= 100ms,开始:179ms 任务05阈值ID:04,等待= 300ms,开始:179ms Task-06 ThrID:07,Wait = 100ms,START:184ms 任务01阈值ID:05,等待= 300ms,开始:178ms task-07 ThrID:10,Wait = 300ms,START:184ms task-00 ThrID:03,Wait = 100ms,END:284ms task-08 ThrID:03,Wait = 100ms,START:184ms 任务02 ThrID:06,等待= 100ms,结束:285ms 任务09阈值ID:06,等待= 300ms,开始:184ms task-04 ThrID:08,Wait = 100ms,END:286ms 任务06 ThrID:07,等待= 100ms,结束:293ms task-08 ThrID:03,Wait = 100ms,END:385ms 任务03 ThrID:09,等待= 300ms,结束:485ms 任务05阈值ID:04,等待= 300ms,结束:486ms task-01 ThrID:05,Wait = 300ms,END:493ms 任务07 ThrID:10,等待= 300ms,结束:494ms task-09 ThrID:06,Wait = 300ms,END:586ms

With Thread.Sleep(): task-00 ThrID: 03, Wait=100ms, START: 0ms task-03 ThrID: 09, Wait=300ms, START: 179ms task-02 ThrID: 06, Wait=100ms, START: 178ms task-04 ThrID: 08, Wait=100ms, START: 179ms task-05 ThrID: 04, Wait=300ms, START: 179ms task-06 ThrID: 07, Wait=100ms, START: 184ms task-01 ThrID: 05, Wait=300ms, START: 178ms task-07 ThrID: 10, Wait=300ms, START: 184ms task-00 ThrID: 03, Wait=100ms, END: 284ms task-08 ThrID: 03, Wait=100ms, START: 184ms task-02 ThrID: 06, Wait=100ms, END: 285ms task-09 ThrID: 06, Wait=300ms, START: 184ms task-04 ThrID: 08, Wait=100ms, END: 286ms task-06 ThrID: 07, Wait=100ms, END: 293ms task-08 ThrID: 03, Wait=100ms, END: 385ms task-03 ThrID: 09, Wait=300ms, END: 485ms task-05 ThrID: 04, Wait=300ms, END: 486ms task-01 ThrID: 05, Wait=300ms, END: 493ms task-07 ThrID: 10, Wait=300ms, END: 494ms task-09 ThrID: 06, Wait=300ms, END: 586ms

修改: 使用async lambda和await Task.Delay()与Thread.Sleep()一样快,可能也更快(511ms). 在循环中进行10次迭代时,使用ThreadPool.SetMinThreads(16, 16);和Task.Delay().Wait()与Thread.Sleep一样快.随着迭代次数的增加,它又变慢了.有趣的是,如果不进行调整,我将Thread.Sleep的迭代次数增加到 30 ,它仍然会更快,然后使用Task.Delay().Wait() 进行 10 迭代. 重载Task.Delay(wait).Wait(wait)的速度与Thread.Sleep()

With async lambda and await Task.Delay() is as fast as Thread.Sleep(), may be also faster (511ms). Edit 2: With ThreadPool.SetMinThreads(16, 16); Task.Delay().Wait() works as fast as Thread.Sleep for 10 iteration in the loop. With more iterations it's slower again. It's also interesting, that if without adjusting I increase the number of iterations for Thread.Sleep to 30, it's still faster, then 10 iteration with Task.Delay().Wait() Edit 3: The overloading Task.Delay(wait).Wait(wait) works as fast as Thread.Sleep()

推荐答案

我稍微重写了一下代码片段以更好地整理结果,但我的全新笔记本电脑的内核太多,无法很好地解释现有的混乱输出.记录每个任务的开始和结束时间,并在完成所有任务后显示它们.并记录任务的实际开始时间.我知道了:

I rewrote the posted snippet a bit to get the results ordered better, my brand-new laptop has too many cores to interpret the existing jumbled output well enough. Recording the start and end times of each task and displaying them after they are all done. And recording the actual start time of the Task. I got:

0: 68 - 5031 1: 69 - 5031 2: 68 - 5031 3: 69 - 5031 4: 69 - 1032 5: 68 - 5031 6: 68 - 5031 7: 69 - 5031 8: 1033 - 5031 9: 1033 - 2032 10: 2032 - 5031 11: 2032 - 3030 12: 3030 - 5031 13: 3030 - 4029 14: 4030 - 5031 15: 4030 - 5031

嗯,这突然变得很有意义.一种在处理线程池线程时始终要注意的模式.请注意,每秒钟发生一次重大事件,并且两个tp线程开始运行,并且其中一些线程可以完成.

Ah, that suddenly makes a lot of sense. A pattern to always watch for when dealing with threadpool threads. Note how once a second something significant happens and two tp threads start running and some of them can complete.

这是一个僵局,类似于此Q + A ,但除此之外,该用户的代码不会带来更灾难性的结果.由于它是埋在.NETFramework代码中,因此原因几乎是不可能的,您必须查看如何实现Task.Delay()才能理解它.

This is a deadlock scenario, similar to this Q+A but otherwise without the more disastrous outcome of that user's code. The cause is next-to-impossible to see since it is buried in .NETFramework code, you'd have to look how Task.Delay() is implemented to make sense of it.

相关代码在此处,请注意它如何使用System.Threading.Timer来实现延迟.关于该计时器的一个棘手的细节是它的回调是在线程池上执行的.这是Task.Delay()可以实现您不为不使用的商品付费"承诺的基本机制.

The relevant code is here, note how it uses a System.Threading.Timer to implement the delay. A gritty detail about that timer is that its callback is executed on the threadpool. Which is the basic mechanism by which Task.Delay() can implement the "you don't pay for what you don't use" promise.

棘手的细节是,如果线程池忙于处理线程池执行请求,则可能要花一些时间.这不是计时器很慢,问题在于回调方法还没有足够快地启动.该程序中的问题Task.Run()添加了一堆请求,超过了可以同时执行的请求数量.之所以会发生死锁,是因为由Task.Run()启动的tp线程在计时器回调执行之前无法完成Wait()调用.

The gritty detail is that this can take a while if the threadpool is busy churning away at threadpool execution requests. It's not the timer is slow, the problem is that the callback method just doesn't get started soon enough. The problem in this program, Task.Run() added a bunch of requests, more than can be executed at the same time. The deadlock occurs because the tp-thread that was started by Task.Run() cannot complete the Wait() call until the timer callback executes.

通过将以下代码添加到Main()的开头,可以使它永久死机,使其永久挂起程序:

You can make it a hard deadlock that hangs the program forever by adding this bit of code to the start of Main():

ThreadPool.SetMaxThreads(Environment.ProcessorCount, 1000);

但是正常的最大线程数要高得多.线程池管理器利用它来解决这种死锁.当现有线程未完成时,它每秒执行一次的线程数将比理想"数量多两个.这就是您在输出中看到的内容.但这一次只有两个,不足以在Wait()调用中阻塞的8个繁忙线程中占用很多凹痕.

But the normal max-threads is much higher. Which the threadpool manager takes advantage of to solve this kind of deadlock. Once a second it allows two more threads than the "ideal" number of them to execute when the existing ones don't complete. That's what you see back in the output. But it is only two at a time, not enough to put much of a dent in the 8 busy threads that are blocked on the Wait() call.

Thread.Sleep()调用不存在此问题,它不依赖于.NETFramework代码或线程池来完成.它是OS线程调度程序来处理的,并且它总是通过时钟中断来运行.因此,允许新的tp线程开始每100或300毫秒执行一次,而不是每秒执行一次.

The Thread.Sleep() call does not have this problem, it doesn't depend on .NETFramework code or the threadpool to complete. It is the OS thread scheduler that takes care of it, and it always runs by virtue of the clock interrupt. Thus allowing new tp threads to start executing every 100 or 300 msec instead of once a second.

很难给出具体建议以避免这种死锁陷阱.除了通用建议外,请始终避免阻塞工作线程.

Hard to give concrete advice to avoid such a deadlock trap. Other than the universal advice, always avoid having worker threads block.

更多推荐

Task.Delay().Wait()发生了什么?

本文发布于:2023-11-08 16:15:14,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1569857.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:发生了什么   Task   Delay   Wait

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!