为什么运行一百个异步任务比运行一百个线程需要更长的时间?
我有以下测试类:
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秒。
为什么?
答案 0 :(得分:12)
Action
方法目前因使用Task.WaitAll(tasks)
而受阻。默认使用Task
时,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, task2, task3, task4, task5};
return Task.WhenAll(tasks);
}
修改TestMethod1
以正确处理新的Task
返回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
运行长时间阻止操作,请确保在创建TaskCreationOptions.LongRunning
实例时使用Task
(这将创建新的基础Thread
而不是使用ThreadPool
)。
ThreadPool
是问题的进一步证据,以下内容也可以缓解您的问题(请勿使用此问题):
ThreadPool.SetMinThreads(500, 500);
这表明新ThreadPool
线程的“缓慢”产生导致了您的瓶颈。
答案 1 :(得分:1)
任务在线程池的线程上执行。线程池作为有限数量的线程被重用。所有任务或所有请求的操作在空闲时由这些线程排队并执行。
假设你的线程池有10个线程,你有100个任务在等待,然后执行10个任务,而其他90个任务只是在队列中等待,直到前10个任务完成。
在第二种测试方法中,您创建了100个专用于其任务的线程。因此,不是10个线程同时运行,而是100个线程正在进行工作。