异步任务陷入等待激活

问题描述 投票:0回答:1

我编写了一个简单的负载测试工具,可以在两秒内向慢速端点发送 1000 个请求。大多数异步任务都会完成,但最终该工具会卡住,因为某些任务卡在 WaitingForActivation 状态,我不明白为什么。

这可能是由于线程池线程耗尽造成的吗?有关如何进一步解决此问题的任何指导。

下面是我的代码。

// See https://aka.ms/new-console-template for more information
using System.Threading;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Concurrent;


CancellationTokenSource cts = new CancellationTokenSource();

await new Client().PerformRequestsAsync("https://urnice.azure-api.net/api-title-proxy/", 1000, 1000, cts.Token);
Console.WriteLine("All Done");
public class Client
{

    public static int completedCount = 0;
    public static int headerCompletedCount = 0;
    public static int allCompletedCount = 0;
    public static int failedCompletedCount = 0;
    public async Task PerformRequestsAsync(string url, int maxConcurrency, int totalRequests, CancellationToken cancellationToken)
    {

            try
            {
                var tasks = new List<Task>();
                for (int i = 0; i < totalRequests; i++)
                {
                    if(i==500)
                        await Task.Delay(1000);

                    // Wait to proceed until it is safe to do so
                    tasks.Add(SendAsync(url, cancellationToken));
                }

                // Wait for all tasks to complete
                await Task.WhenAll(tasks);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"An error occurred: {ex.Message}");
            }
            finally
            {
                // Release the semaphore whether success or fail
            }
        
    }

    async Task SendAsync(string url, CancellationToken cancellationToken)
    {
        try
        {
            using (var httpClient = new HttpClient())
            {
                // Send the request and instruct HttpClient to complete as soon as headers are read
                var request = new HttpRequestMessage(HttpMethod.Get, url);
                var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
                Console.WriteLine("HCC: " + Interlocked.Increment(ref headerCompletedCount) + " BCC: " + completedCount + " ACC: " + allCompletedCount + " FCC: " + failedCompletedCount);

                Console.WriteLine($"Headers received: {response.StatusCode}");

                // Now read the response body in chunks of 64 KB
                const int bufferSize = 64 * 1024; // 64 KB
                var buffer = new byte[bufferSize];
                var totalRead = 0;

                using (var responseStream = await response.Content.ReadAsStreamAsync())
                {
                    int bytesRead;
                    while ((bytesRead = await responseStream.ReadAsync(buffer, 0, buffer.Length, cancellationToken)) > 0)
                    {
                        totalRead += bytesRead;
                        //Console.WriteLine($"Read {bytesRead} bytes this chunk, total {totalRead} bytes read.");
                        // Process the chunk as needed (omitted for brevity)
                    }
                }
                Console.WriteLine("BCC: " + Interlocked.Increment(ref completedCount) + " HCC: " + headerCompletedCount + " ACC: " + allCompletedCount + " FCC: " + failedCompletedCount);


                Console.WriteLine("Completed reading the response.");

                Console.WriteLine("ACC: " + Interlocked.Increment(ref allCompletedCount) + " BCC: " + completedCount + " HCC: " + headerCompletedCount + " FCC: " + failedCompletedCount);

            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"An error occurred: {ex.Message}");
            Console.WriteLine("FCC: " + Interlocked.Increment(ref failedCompletedCount) + " BCC: " + completedCount + " HCC: " + headerCompletedCount + " ACC: " + allCompletedCount);
        }
    }
}

程序有时会冻结,输出如下所示

输出将以这样的方式开始

FCC:2 BCC:3 HCC:997 ACC:3
发生错误:无法从传输连接读取数据:现有连接被远程主机强制关闭..
FCC:3 BCC:3 HCC:997 ACC:3
BCC:4 HCC:997 ACC:3 FCC:3
已阅读完回复。
ACC:4 BCC:4 HCC:997 FCC:3
HCC:998 BCC:4 ACC:4 FCC:3
收到标头:好的
HCC:999 BCC:4 ACC:4 FCC:3
收到标头:好的
HCC:1000 BCC:4 ACC:4 FCC:3
收到标头:好的
BCC:5 HCC:1000 ACC:4 FCC:3
已阅读完回复。
ACC:5 BCC:5 HCC:1000 FCC:3
发生错误:无法从传输连接读取数据:现有连接被远程主机强制关闭..

.
.
.
.
.
.
.

然后用下面的东西冻结

ACC:980 BCC:980 HCC:1000 FCC:5
BCC:981 HCC:1000 ACC:980 FCC:5
已阅读完回复。
ACC:981 BCC:981 HCC:1000 FCC:5
BCC:982 HCC:1000 ACC:981 FCC:5
已阅读完回复。
ACC:982 BCC:982 HCC:999 FCC:5

c# asynchronous async-await
1个回答
0
投票

您一次发出了太多请求,而没有等待任何一个请求。在某些时候,服务器会说“这个人没有响应,让我们关闭套接字”。

您需要使用

Task.Run
将发送交给线程池来处理。这给您留下了一个单独的循环,可以忙于添加更多请求。

var tasks = new List<Task>();
for (int i = 0; i < totalRequests; i++)
{
    if (i % 500 == 499)   // delay after only the first 500 or after every 500??
        await Task.Delay(1000);

    // Wait to proceed until it is safe to do so
    tasks.Add(Task.Run(() => SendAsync(url, cancellationToken), cancellationToken));
}

// Wait for all tasks to complete
await Task.WhenAll(tasks);

此外,您的

SendAsync
函数缺少
using
。缺少的
using
也可能是导致问题的原因,因为它不能保证正确的清理。还有每次使用新的
HttpClient
,这可能会导致套接字耗尽。

static HttpClient _httpClient = new HttpClient();

而且它可能会更有效率。目前尚不清楚您正在使用该读取循环做什么,但您也许可以使用

stream.CopyToAsync

© www.soinside.com 2019 - 2024. All rights reserved.