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