C# · 12月 30, 2021

c# – 为什么运行一百个异步任务需要比运行一百个线程更长的时间?

为什么运行一百个异步任务需要比运行一百个线程更长的时间?

我有以下测试类:

public class AsyncTests{ public void TestMethod1() { var tasks = new List<Task>(); for (var i = 0; i < 100; i++) { var task = new Task(Action); tasks.Add(task); task.Start(); } Task.WaitAll(tasks.ToArray()); } public void TestMethod2() { var threads = new List<Thread>(); for (var i = 0; i < 100; i++) { var thread = new Thread(Action); threads.Add(thread); thread.Start(); } foreach (var thread in threads) { thread.Join(); } } private void Action() { var task1 = LongRunningOperationAsync(); var task2 = LongRunningOperationAsync(); var task3 = LongRunningOperationAsync(); var task4 = LongRunningOperationAsync(); var task5 = LongRunningOperationAsync(); Task[] tasks = {task1,task2,task3,task4,task5}; Task.WaitAll(tasks); } public async Task<int> LongRunningOperationAsync() { var sw = Stopwatch.StartNew(); await Task.Delay(500); Debug.WriteLine(“Completed at {0},took {1}ms”,DateTime.Now,sw.Elapsed.TotalMilliseconds); return 1; }}

尽可能地告诉我们,TestMethod1和TestMethod2应该完全一样.一个使用TPL,两个使用普通的香草线程.一个需要1:30分钟,两次需要0.54秒.

为什么?

解决方法 Action方法正在使用Task.WaitAll(任务)阻止.当使用任务默认情况下,ThreadPool将被用于执行,这意味着您正在阻止共享的ThreadPool线程.

尝试以下内容,您将看到相同的效果:

添加一个非阻塞的Action实现,我们称之为ActionAsync

private Task ActionAsync(){ var task1 = LongRunningOperationAsync(); var task2 = LongRunningOperationAsync(); var task3 = LongRunningOperationAsync(); var task4 = LongRunningOperationAsync(); var task5 = LongRunningOperationAsync(); Task[] tasks = {task1,task5}; return Task.WhenAll(tasks);}

>修改TestMethod1以正确处理新的任务返回的ActionAsync方法

public void TestMethod1(){ var tasks = new List<Task>(); for (var i = 0; i < 100; i++) { tasks.Add(Task.Run(new Func<Task>(ActionAsync))); } Task.WaitAll(tasks.ToArray()); }

你的性能很慢的原因是因为ThreadPool会“慢慢地”产生新的线程,如果你要阻止它可用的几个线程,你会遇到明显的减速.这就是为什么ThreadPool仅用于运行短任务的原因.

如果您打算使用Task运行长时间阻塞操作,那么在创建Task实例时,请确保使用TaskCreationOptions.LongRunning(这将创建一个新的底层Thread而不是使用ThreadPool).

ThreadPool的一些进一步证据是问题,以下也可以减轻你的问题(不要使用这个):

ThreadPool.SetMinThreads(500,500);

这表明新的ThreadPool线程的“慢”产生导致您的瓶颈.