C# 等待任务。延迟(foo);需要秒而不是毫秒
在C# 等待任务。延迟(foo);需要秒而不是毫秒,c#,.net,task-parallel-library,async-await,C#,.net,Task Parallel Library,Async Await,在任务中使用可变延迟。延迟与类似IO的操作结合使用时,随机花费秒而不是毫秒 要复制的代码: using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace ConsoleApplication { cl
任务中使用可变延迟。延迟
与类似IO的操作结合使用时,随机花费秒而不是毫秒
要复制的代码:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication {
class Program {
static void Main(string[] args) {
Task[] wait = {
new delayTest().looper(5250, 20),
new delayTest().looper(3500, 30),
new delayTest().looper(2625, 40),
new delayTest().looper(2100, 50)
};
Task.WaitAll(wait);
Console.WriteLine("All Done");
Console.ReadLine();
}
}
class delayTest {
private Stopwatch sw = new Stopwatch();
public delayTest() {
sw.Start();
}
public async Task looper(int count, int delay) {
var start = sw.Elapsed;
Console.WriteLine("Start ({0}, {1})", count, delay);
for (int i = 0; i < count; i++) {
var before = sw.Elapsed;
var totalDelay = TimeSpan.FromMilliseconds(i * delay) + start;
double wait = (totalDelay - sw.Elapsed).TotalMilliseconds;
if (wait > 0) {
await Task.Delay((int)wait);
SpinWait.SpinUntil(() => false, 1);
}
var finalDelay = (sw.Elapsed - before).TotalMilliseconds;
if (finalDelay > 30 + delay) {
Console.WriteLine("Slow ({0}, {1}): {4} Expected {2:0.0}ms got {3:0.0}ms", count, delay, wait, finalDelay, i);
}
}
Console.WriteLine("Done ({0}, {1})", count, delay);
}
}
}
这张照片是:
睡眠:11.87秒
(实际上,它提供了99%的20毫秒延迟,这些延迟被忽略)
这一延迟比预期的时间长近600倍。相同的延迟同时发生在3个单独的线程上,它们也同时继续
在短任务完成后约40秒,60秒睡眠任务将正常唤醒
有一半的时候,这个问题甚至没有发生。另一半,它有11.5-12秒的一致延迟。我怀疑是调度或线程池问题,但所有线程都应该是空闲的
当我在卡住阶段暂停程序时,主线程stacktrace位于任务上。WaitAll
,3个任务安排在等待任务上。Delay(20)
,一个任务安排在等待任务上。Delay(60000)
。另外还有4个任务在等待前4个任务,报告诸如“Task 24”正在等待此对象:“Task 5313”(由线程0拥有)之类的内容。所有4个任务都表示等待的任务属于线程0。还有4个我认为可以忽略的连续任务
还有其他一些事情正在进行,比如第二个控制台应用程序写入网络流,但是一个控制台应用程序不应该影响另一个
这件事我完全不懂。发生了什么事?
更新:
根据评论和问题:
当我运行我的程序4次时,2-3次它将挂起10-15秒,1-2次它将正常运行(并且不会打印“Sleep:{0:0.00}s”。)
Thread.Count
确实会增加,但不管挂起情况如何,这种情况都会发生。我只是在没有挂起的地方运行了一次线程。计数从24开始,在1秒钟后上升到40,大约22秒短任务正常完成,然后在接下来的40秒内缓慢下降到22
更多的代码,完整的代码可以在下面的链接中找到。启动客户端:
List<Task> tasks = new List<Task>();
private void makeClient(int delay, int startDelay) {
Task task = new ClientConnection(this, delay, startDelay).connectAsync();
task.ContinueWith(_ => {
lock (tasks) { tasks.Remove(task); }
});
lock (tasks) { tasks.Add(task); }
}
private void start() {
DateTime start = DateTime.Now;
Console.WriteLine("Starting clients...");
int[] iList = new[] {
0,1,1,2,
10, 20, 30, 40};
foreach (int delay in iList) {
makeClient(delay, 0); ;
}
makeClient(15, 40);
Console.WriteLine("Done making");
tasks.Add(displayThreads());
waitForTasks(tasks);
Console.WriteLine("All done.");
}
private static void waitForTasks(List<Task> tasks) {
Task[] waitFor;
lock (tasks) {
waitFor = tasks.ToArray();
}
Task.WaitAll(waitFor);
}
List tasks=newlist();
私有void makeClient(int delay、int startDelay){
Task Task=newclientconnection(this,delay,startDelay).connectAsync();
task.ContinueWith(=>{
锁定(任务){tasks.Remove(任务);}
});
锁定(任务){tasks.Add(任务);}
}
私有void start(){
DateTime start=DateTime.Now;
Console.WriteLine(“启动客户端…”);
int[]iList=new[]{
0,1,1,2,
10, 20, 30, 40};
foreach(iList中的int延迟){
makeClient(延迟,0);
}
makeClient(15,40);
控制台。写线(“完成制作”);
添加(displayThreads());
等待任务(任务);
控制台。WriteLine(“全部完成”);
}
私有静态void waitfortask(列出任务){
任务[]等待;
锁定(任务){
waitFor=tasks.ToArray();
}
Task.WaitAll(waitFor);
}
另外,我尝试用wait Task.Run(()=>Thread.Sleep(20))替换Delay(20)
Thread.Count
现在从29变为43,再回到24,但是在多个符文中它从不挂起
有或没有ThreadPool.SetMinThreads(500500)
,使用它不会挂起。(也就是说,即使切换一行代码,有时也会使它停止挂起,只是在我重新启动项目4次后随机继续,但我已经连续尝试了6次,现在没有任何问题)
到目前为止,我已经尝试了上面的所有方法,包括ThreadPool和ThreadPool
更新2:如果没有看到更多的代码,很难做出进一步的猜测,但我想总结一下评论,它可能会在将来帮助其他人:
- 我们已经发现这不是一个问题,因为
ThreadPool.SetMinThreads(500500)
没有帮助
- 您的任务工作流中是否有任何
SynchronizationContext
?将Debug.Assert(SyncrhonizationContext.Current==null)
放在所有地方以检查是否存在此问题。将ConfigureAwait(false)
与每个await
一起使用
- 您的代码中是否有任何
.Wait
,.WaitOne
,.WaitAll
,WaitAny
,。Result
?任何lock(){…}
构造<代码>监视器。进入/退出
或任何其他阻塞同步原语
关于这一点:我已经用Task.Yield()替换了Task.Delay(20)
;Thread.Sleep(20)
作为一种解决方法,它是有效的。但是,是的,我继续试图弄清楚这里发生了什么,因为Task.Delay(20)可能会让它完全无法使用
这听起来确实令人担忧。任务中不太可能有bug。延迟,但一切都有可能。为了进行实验,请尝试将wait Task.Delay(20)
替换为wait Task.Run(()=>Thread.Sleep(20))
,使ThreadPool.SetMinThreads(500,500)
仍保持不变
我还有一个Delay
的实验性实现,它使用未经管理的API(不同于Task.Delay
,它使用System.Threading.Timer
,后者反过来使用托管TimerQueue
)。有空。可以将其作为TaskExt.Delay
而不是标准的Task.Delay
进行尝试。计时器回调被发布到ThreadPool
,因此ThreadPool.SetMinThreads(500500)
仍应用于此实验。我怀疑这会有什么不同,但我很想知道
请展示一个简短但完整的程序来演示问题-这样会更容易帮助您。(我们目前对您的环境一无所知…)现在
List<Task> tasks = new List<Task>();
private void makeClient(int delay, int startDelay) {
Task task = new ClientConnection(this, delay, startDelay).connectAsync();
task.ContinueWith(_ => {
lock (tasks) { tasks.Remove(task); }
});
lock (tasks) { tasks.Add(task); }
}
private void start() {
DateTime start = DateTime.Now;
Console.WriteLine("Starting clients...");
int[] iList = new[] {
0,1,1,2,
10, 20, 30, 40};
foreach (int delay in iList) {
makeClient(delay, 0); ;
}
makeClient(15, 40);
Console.WriteLine("Done making");
tasks.Add(displayThreads());
waitForTasks(tasks);
Console.WriteLine("All done.");
}
private static void waitForTasks(List<Task> tasks) {
Task[] waitFor;
lock (tasks) {
waitFor = tasks.ToArray();
}
Task.WaitAll(waitFor);
}