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

C# 基于任务的并发比直接使用System.Threading.Thread慢得多,c#,multithreading,task-parallel-library,dotnet-httpclient,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")));

我一直在使用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());
var taskArray=newlist();
对于(int i=0;i<105;i++)
taskArray.Add(Task.Factory.StartNew(()=>Get(“/content/test.jpg”));
Task.WaitAll(taskArray.ToArray());
尽管每个请求(通过fiddler监控时)只需要15毫秒左右的时间就可以执行,但当请求时间超过默认超时100秒时,我会收到HttpClient抛出的超时异常(TaskCanceledExceptions,这是同一件事)

我尝试的第一件事是增加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();
var handle=neweventwaithandle(false,EventResetMode.ManualReset);
对于(int i=0;i<105;i++)
{
var t=新线程(()=>
{
Get(“/content/test.jpg”);
if(联锁减量(参考编号任务)==0)
handle.Set();
});
t、 Start();
}
handle.WaitOne();
这就像预期的一样!我甚至可以将线程数增加到2000个,它比基于任务的版本发送105个线程的速度更快


给出了什么?

正如Matthew所提到的,这是因为任务工厂默认使用线程池。如果要使用任务并提高线程池限制,稍微提高限制或创建自己的线程池可能会对您有所帮助

也就是说,您已经在使用一个拥有所有异步方法的类:为什么要尝试将这些方法包装到线程中?只需捕获任务并等待它们

大概是这样的:

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

Task.WaitAll(tasks.ToArray());
var tasks=newlist();
对于(int i=0;i<105;i++)
添加(client.GetAsync(uri));
Task.WaitAll(tasks.ToArray());

发布获取代码。您是否增加了.NET HTTP限制?获取代码是:httpClient.GetAsync(url)。结果现在我已经看到了这一点,也许我应该尝试等待该操作,而不是阻塞结果……应该已经发现了!是的,我曾尝试通过ServicePointManager提高限制,但没有帮助。值得注意的是,并行发送1000个请求可能不是一个好主意。最好使用一种并行异步foreach习惯用法。但这可能是正确的答案。我将它们包装在线程中,因为我需要请求尽可能并发,使用异步,而后续请求将在接收到前一个请求的响应之前发送,并行度将低于线程。@usr这不是生产场景,这是一个负载测试场景。我尝试并行发送请求的原因是,我们在生产中看到了一个问题,这种类型的使用模式会导致偶尔的异常,因此我尝试对这种模式进行一些测试。正是由于请求几乎在同一时间发生,才出现了问题。@SamShiles好的,异步IO允许您发送更多的并行请求,而不是更少的请求。它需要更少的资源。线程需要一段时间才能启动,而异步IO的启动速度非常快。在10k线程的情况下,Windows调度程序开始表现出不稳定的行为,我不会接近这个数量。有趣的是,我将玩一个游戏并报告我的发现!非常感谢你的意见。