Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/three.js/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 要同步到异步的异步工作具有奇怪的行为_C#_.net_Asynchronous_Async Await - Fatal编程技术网

C# 要同步到异步的异步工作具有奇怪的行为

C# 要同步到异步的异步工作具有奇怪的行为,c#,.net,asynchronous,async-await,C#,.net,Asynchronous,Async Await,我有一个遗留项目,有很多IoC和HttpClient调用。为了提高性能,我尝试使用TPL来并行化工作。但情况变得更糟了 总之,我们尝试将封装异步方法的同步方法并行化。 重构后,性能更好,但我不理解这种行为 我制作了这个最小的代码示例来重现.NET 4.7控制台项目中的行为: 类程序 { 静态void Main(字符串[]参数) { var tasks=新列表(); 对于(int i=0;iWorkSync(n)); 睡眠(TimeSpan.From毫秒(1)); } Task.WaitAll(t

我有一个遗留项目,有很多IoC和
HttpClient
调用。为了提高性能,我尝试使用TPL来并行化工作。但情况变得更糟了

总之,我们尝试将封装异步方法的同步方法并行化。 重构后,性能更好,但我不理解这种行为

我制作了这个最小的代码示例来重现.NET 4.7控制台项目中的行为:

类程序
{
静态void Main(字符串[]参数)
{
var tasks=新列表();
对于(int i=0;i<15;i++)
{
var n=i;
tasks.Add(Task.Run(()=>WorkSync(n));
睡眠(TimeSpan.From毫秒(1));
}
Task.WaitAll(tasks.ToArray());
}
专用静态void工作同步(int i)
{
Debug.WriteLine($“{i:000}\t{DateTime.Now:HH:mm:ss.fff}\tStartA”);
WorkAsync(i).GetAwaiter().GetResult();
WriteLine($“{i:000}\t{DateTime.Now:HH:mm:ss.fff}\tFinishA”);
}
专用静态异步任务WorkAsync(int i)
{
Debug.WriteLine($“{i:000}\t{DateTime.Now:HH:mm:ss.fff}\tStartB”);
等待任务。运行(()=>Work(i));
Debug.WriteLine($“{i:000}\t{DateTime.Now:HH:mm:ss.fff}\tFinishB”);
}
私人静态无效工作(int i)
{
Debug.WriteLine($“{i:000}\t{DateTime.Now:HH:mm:ss.fff}\tDo某物”);
}
}
结果:

004 11:30:10.629    StartA
000 11:30:10.627    StartA
002 11:30:10.627    StartA
001 11:30:10.627    StartA
003 11:30:10.627    StartA
005 11:30:10.628    StartA
006 11:30:10.628    StartA
007 11:30:10.628    StartA
008 11:30:10.633    StartA
002 11:30:10.692    StartB
001 11:30:10.692    StartB
000 11:30:10.692    StartB
003 11:30:10.692    StartB
005 11:30:10.692    StartB
004 11:30:10.692    StartB
006 11:30:10.695    StartB
007 11:30:10.699    StartB
008 11:30:10.703    StartB
009 11:30:11.632    StartA
009 11:30:11.633    StartB
010 11:30:12.616    StartA
010 11:30:12.617    StartB
011 11:30:13.612    StartA
011 11:30:13.613    StartB
012 11:30:14.612    StartA
012 11:30:14.613    StartB
013 11:30:15.612    StartA
013 11:30:15.613    StartB
014 11:30:16.611    StartA
014 11:30:16.612    StartB
002 11:30:17.612    Do Something
002 11:30:17.614    FinishB
002 11:30:17.615    FinishA
001 11:30:17.615    Do Something
001 11:30:17.657    FinishB
006 11:30:17.658    Do Something
005 11:30:17.636    Do Something
006 11:30:17.680    FinishB
005 11:30:17.701    FinishB
007 11:30:17.723    Do Something
001 11:30:17.658    FinishA
005 11:30:17.744    FinishA
004 11:30:17.744    Do Something
007 11:30:17.765    FinishB
004 11:30:17.808    FinishB
006 11:30:17.723    FinishA
007 11:30:17.830    FinishA
003 11:30:17.894    Do Something
013 11:30:17.786    Do Something
003 11:30:17.895    FinishB
013 11:30:17.917    FinishB
012 11:30:17.919    Do Something
008 11:30:17.830    Do Something
014 11:30:17.788    Do Something
004 11:30:17.851    FinishA
009 11:30:17.851    Do Something
013 11:30:17.922    FinishA
000 11:30:17.872    Do Something
003 11:30:17.918    FinishA
012 11:30:17.927    FinishB
010 11:30:17.922    Do Something
008 11:30:17.931    FinishB
014 11:30:17.933    FinishB
011 11:30:17.955    Do Something
008 11:30:18.046    FinishA
009 11:30:17.958    FinishB
009 11:30:18.111    FinishA
014 11:30:18.068    FinishA
000 11:30:17.980    FinishB
000 11:30:18.114    FinishA
010 11:30:18.024    FinishB
011 11:30:18.089    FinishB
012 11:30:18.003    FinishA
011 11:30:18.138    FinishA
010 11:30:18.116    FinishA

Work
方法仅在主系统中启动所有任务后执行。 我已使用调试器对此进行了检查,阻塞的不是调试显示

1) 我不明白这个日程安排。 你能解释一下原因吗

2) 前10项任务开始得很快,但后5项任务开始得很慢。 你能解释一下原因吗

我不明白这个日程安排。你能解释一下原因吗

你有一个紧密的循环来启动一堆任务。它们都启动了,但每个都启动了另一个线程。直到启动线程调用Debug.WriteLine($“{i:000}\tFinishB”)

Work()
线程被阻塞的一个原因是
Debug.WriteLine()
获取了一个锁-因此,如果其他线程当前正在写入以调试
Work()
线程将被阻塞。其寓意是,
Debug.WriteLine()
可以改变多线程的行为,因为它使用锁


前10项任务的启动速度非常快,但最后5项任务的启动速度非常慢。你能解释一下原因吗

然而,发生这种情况还有另一个更重要的原因:线程池的“最小线程限制”

线程池保持等待运行的最小线程数。您可以通过以下代码查看该值:

ThreadPool.GetMinThreads(out int workers, out int ports);
Console.WriteLine(workers);  // Prints 8 on my system.
现在需要知道的重要一点是,如果需要的线程数量超过了最小数量,那么只有在延迟几百毫秒后才会创建新的线程(不确定具体需要多长时间,但似乎大约需要一秒钟)

因此,除了在
Debug.WriteLine()
实现中由锁引起的阻塞之外,还发生了以下情况:

  • 启动了大量任务,消耗的线程池大小超过了最小线程池大小,因此在启动前几个任务后,新任务之间会引入延迟
  • 由于这种延迟,当开始执行
    Work()
    任务时,它就被延迟了。这导致它比其他情况下启动得晚得多
您可以通过在测试代码开始时增加最小线程池线程数并观察输出中的差异来证明这一点

要尝试此操作,请在启动任何任务之前添加以下代码行:

ThreadPool.SetMinThreads(100, 100);
当我尝试这样做时,所有任务都会更快地启动,并且一些“Do Something”消息会在所有其他任务启动之前出现(而您注意到,以前这些消息只会在所有其他任务启动之后出现)

注意微软:

您可以使用ThreadPool.SetMinThreads方法增加空闲线程的最小数量。但是,不必要地增加这些值可能会导致性能问题。如果同时启动的任务太多,则所有任务都可能看起来很慢。在大多数情况下,线程池使用其自己的线程分配算法将执行得更好


这里需要注意的是:控制台的输出不一定遵循异步代码执行的顺序或时间。您可能最终会追逐一条红鲱鱼……
Work
方法会在最后执行,因为您会同时启动所有任务,这将创建多个线程,这些线程将并行运行。out没什么奇怪的吗?你想完成什么?@TheodorZoulias,我编辑了这个问题以输出时间信息。@或者我也会同时调用HttpClient,以对同一服务执行1000多个请求,每次5个。我不使用Task.RunAnywhere,也不使用类似这样的任务循环。为此,在.NET旧版中,我必须将
servicePointManager.DefaultConnectionLimit
增加到默认值2以上。如果我替换
Debug.WriteLine
call by
ConcurrentBag.Add
并显示包,则我还可以使用ActionBlock之类的类使编码和可控节流变得更加容易。你说得对,这不是调度问题,而是资源分配问题。