基于任务的并发比直接使用System.Threading.Thread慢得多

时间:2015-06-25 14:08:01

标签: c# multithreading task-parallel-library dotnet-httpclient

我一直在使用System.Threading.Task和System.Net.Http.HttpClient来加载测试Web服务器并观察到一些奇怪的行为。这是代码:

var taskArray = new List<Task>();              
for (int i = 0; i < 105; i++)
    taskArray.Add(Task.Factory.StartNew(() => Get("/content/test.jpg")));

Task.WaitAll(taskArray.ToArray());

即使每个请求(通过fiddler监视),只花了大约15ms来执行,我得到了超时异常(好吧,TaskCanceledExcpetions - 这是同样的事情)由HttpClient抛出,用于发出请求时请求时间超过默认超时100秒。

我尝试的第一件事就是增加了HttpClient的超时功能,但是我还在努力理解为什么响应时间非常短的请求超时。所以我在调用HttpClient.PostAsync之前设置了一个计时器,并检查完成所需的时间,尽管服务器发送的响应更加迅速,但可疑时间超过100秒。

然后我读到HttpClient.Timeout是整个异步操作的超时,这让我觉得可能是任务调度程序只是在响应准备就绪后执行了收到响应的异步回调而导致我出现问题收到。

考虑到这一点,我决定使用旧的System.Threading.Thread编写代码:

var handle = new EventWaitHandle(false, EventResetMode.ManualReset);
for (int i = 0; i < 105; i++)
{
    var t = new Thread(() =>
    {
        Get("/content/test.jpg");
        if (Interlocked.Decrement(ref numberOfTasks) == 0)
            handle.Set();
    });
    t.Start();
}

handle.WaitOne();

这按预期工作!我甚至可以将线程数增加到2000,并且它比基于任务的版本发送105更快地完成所有线程!

是什么给出了?

1 个答案:

答案 0 :(得分:2)

正如Matthew所说,这是因为Task工厂默认使用线程池。如果要使用任务并希望提高线程池限制,稍微提高限制或创建自己的线程池可能会有一些价值。

那就是说,你已经在使用一个拥有所有异步方法的类了:为什么要尝试将它们包装在线程中?只需抓住任务并等待它们。

这样的事情:

var tasks = new List<Task>();              
for (int i = 0; i < 105; i++)
    tasks.Add(client.GetAsync(uri));

Task.WaitAll(tasks.ToArray());