我很困惑,为什么 Task.Delay().Wait() 需要 4 倍的时间,然后是 Thread.Sleep()?
例如task-00 是否在仅线程 9 上运行并花费了 2193 毫秒? 我知道,同步等待在任务中很糟糕,因为整个线程都被阻塞了。仅供测试。
控制台应用程序中的简单测试:
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():
任务 03 ThrID:05,等待 = 300 毫秒,开始:184 毫秒
任务 04 ThrID:07,等待 = 100 毫秒,开始:184 毫秒
task-00 ThrID: 09, Wait=100ms, START: 0ms
任务 06 ThrID:04,等待 = 100 毫秒,开始:185 毫秒
任务 01 ThrID:08,等待 = 300 毫秒,开始:183 毫秒
任务 05 ThrID:03,等待 = 300 毫秒,开始:185 毫秒
任务 02 ThrID:06,等待 = 100 毫秒,开始:184 毫秒
任务 07 ThrID:10,等待 = 300 毫秒,开始:209 毫秒
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
任务 05 ThrID:03,等待 = 300 毫秒,结束:2193 毫秒
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,等待 = 300 毫秒,开始:179 毫秒
任务 02 ThrID:06,等待 = 100 毫秒,开始:178 毫秒
任务 04 ThrID:08,等待 = 100 毫秒,开始:179 毫秒
任务 05 ThrID:04,等待 = 300 毫秒,开始:179 毫秒
任务 06 ThrID:07,等待 = 100 毫秒,开始:184 毫秒
任务 01 ThrID:05,等待 = 300 毫秒,开始:178 毫秒
任务 07 ThrID:10,等待 = 300 毫秒,开始:184 毫秒
task-00 ThrID: 03, Wait=100ms, END: 284ms
任务 08 ThrID:03,等待 = 100 毫秒,开始:184 毫秒
task-02 ThrID: 06, Wait=100ms, END: 285ms
任务 09 ThrID:06,等待 = 300 毫秒,开始:184 毫秒
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
任务 01 ThrID:05,等待 = 300 毫秒,结束:493 毫秒
task-07 ThrID: 10, Wait=300ms, END: 494ms
task-09 ThrID: 06, Wait=300ms, END: 586ms
编辑:
使用 async lambda 和 await Task.Delay() 与 Thread.Sleep() 一样快,可能是也更快(511 毫秒)。
编辑 2:
使用 ThreadPool.SetMinThreads(16, 16); Task.Delay().Wait() 与 Thread.Sleep 一样快 10 次迭代在循环。随着更多的迭代,它又变慢了。这也很有趣,如果不调整我将 Thread.Sleep 的迭代次数增加到 30,它仍然更快,然后 10 迭代Task.Delay().Wait()
编辑 3:
重载 Task.Delay(wait).Wait(wait) 的工作速度与 Thread.Sleep()
最佳答案
我稍微重写了发布的代码片段,以便更好地排序结果,我全新的笔记本电脑有太多内核,无法很好地解释现有的困惑输出。记录每项任务的开始和结束时间,并在全部完成后显示。并记录Task的实际开始时间。我得到了:
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 线程开始运行,其中一些可以完成。
这是一个死锁场景,类似于this Q+A但除此之外,该用户的代码不会造成更灾难性的后果。原因几乎是不可能的,因为它隐藏在 .NETFramework 代码中,您必须查看 Task.Delay() 的实现方式才能理解它。
相关代码is here ,请注意它如何使用 System.Threading.Timer 来实现延迟。关于该计时器的一个重要细节是它的回调是在线程池上执行的。这是 Task.Delay() 可以实现“你不用为你不用的东西付费” promise 的基 native 制。
具体细节是,如果线程池正忙于处理线程池执行请求,这可能需要一段时间。不是计时器慢,问题是回调方法启动不够快。这个程序中的问题,Task.Run() 增加了一堆请求,超过了可以同时执行的请求。死锁的发生是因为由 Task.Run() 启动的 tp 线程在计时器回调执行之前无法完成 Wait() 调用。
您可以通过将这段代码添加到 Main() 的开头来使其成为一个使程序永远挂起的硬死锁:
ThreadPool.SetMaxThreads(Environment.ProcessorCount, 1000);
但正常的最大线程要高得多。线程池管理器利用它来解决这种死锁。当现有线程未完成时,它每秒允许执行比“理想”数量多两个线程。这就是您在输出中看到的内容。但它一次只有两个,不足以对 Wait() 调用中阻塞的 8 个繁忙线程造成太大影响。
Thread.Sleep() 调用没有这个问题,它不依赖于.NETFramework 代码或线程池来完成。它是 OS 线程调度程序来处理它,并且它始终依靠时钟中断运行。因此允许新的 tp 线程每 100 或 300 毫秒开始执行一次,而不是每秒一次。
很难给出避免这种死锁陷阱的具体建议。除了通用建议外,始终避免阻塞工作线程。
关于c# - Task.Delay().Wait() 是怎么回事?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53648014/
下面例子中的Nested和Child有什么区别?是否只是同一事物的不同语法?classParentclassNested...endendclassChild 最佳答案 不,它们是不同的。嵌套:Computer之外的“Processor”类只能作为Computer::Processor访问。嵌套为内部类(namespace)提供上下文。对于ruby解释器Computer和Computer::Processor只是两个独立的类。classComputerclassProcessor#Tocreateanobjectforthisc
如何在ruby中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL
所以我开始关注ruby,很多东西看起来不错,但我对隐式return语句很反感。我理解默认情况下让所有内容返回self或nil但不是语句的最后一个值。对我来说,它看起来非常脆弱(尤其是)如果你正在使用一个不打算返回某些东西的方法(尤其是一个改变状态/破坏性方法的函数!),其他人可能最终依赖于一个返回对方法的目的并不重要,并且有很大的改变机会。隐式返回有什么意义?有没有办法让事情变得更简单?总是有返回以防止隐含返回被认为是好的做法吗?我是不是太担心这个了?附言当人们想要从方法中返回特定的东西时,他们是否经常使用隐式返回,这不是让你组中的其他人更容易破坏彼此的代码吗?当然,记录一切并给出
我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha
给定以下方法:defsome_method:valueend以下语句按我的预期工作:some_method||:other#=>:valuex=some_method||:other#=>:value但是下面语句的行为让我感到困惑:some_method=some_method||:other#=>:other它按预期创建了一个名为some_method的局部变量,随后对some_method的调用返回该局部变量的值。但为什么它分配:other而不是:value呢?我知道这可能不是一件明智的事情,并且可以看出它可能有多么模棱两可,但我认为应该在考虑作业之前评估作业的右侧...我已经在R
我在我的Rails3示例应用程序上使用CarrierWave。我想验证远程位置上传,因此当用户提交无效URL(空白或非图像)时,我不会收到标准错误异常:CarrierWave::DownloadErrorinImageController#createtryingtodownloadafilewhichisnotservedoverHTTP这是我的模型:classPaintingtrue,:length=>{:minimum=>5,:maximum=>100}validates:image,:presence=>trueend这是我的Controller:classPaintingsC
电脑0x0000001A蓝屏错误怎么U盘重装系统教学分享。有用户电脑开机之后遇到了系统蓝屏的情况。系统蓝屏问题很多时候都是系统bug,只有通过重装系统来进行解决。那么蓝屏问题如何通过U盘重装新系统来解决呢?来看看以下的详细操作方法教学吧。 准备工作: 1、U盘一个(尽量使用8G以上的U盘)。 2、一台正常联网可使用的电脑。 3、ghost或ISO系统镜像文件(Win10系统下载_Win10专业版_windows10正式版下载-系统之家)。 4、在本页面下载U盘启动盘制作工具:系统之家U盘启动工具。 U盘启动盘制作步骤: 注意:制作期间,U盘会被格式化,因此U盘中的重要文件请注
C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.
s=Socket.new(Socket::AF_INET,Socket::SOCK_STREAM,0)s.connect(Socket.pack_sockaddr_in('port','hostname'))ssl=OpenSSL::SSL::SSLSocket.new(s,sslcert)ssl.connect从这里开始,如果ssl连接和底层套接字仍然是ESTABLISHED,或者它是否在默认值7200之后进入CLOSE_WAIT,我想检查一个线程几秒钟甚至更糟的是在实际上不需要.write()或.read()的情况下关闭。是用select()、IO.select()还是其他方法完成
我正在研究使用EventMachine支持的twitter-streamrubygem来跟踪和捕获推文。我对整个事件编程有点陌生。我如何判断我在事件循环中所做的任何处理是否导致我落后?有没有简单的检查方法? 最佳答案 您可以通过使用周期性计时器并打印出耗时来确定延迟。如果您使用的是1秒的计时器,您应该已经过了大约1秒,如果它更长,您就知道您正在减慢react器的速度。@last=Time.now.to_fEM.add_periodic_timer(1)doputs"LATENCY:#{Time.now.to_f-@last}"@