Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/291.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在一行中调用多个异步任务时,异步代码似乎部分阻塞(使用HttpClient)_C#_Asynchronous_Async Await_Task_Blocking - Fatal编程技术网

C# 在一行中调用多个异步任务时,异步代码似乎部分阻塞(使用HttpClient)

C# 在一行中调用多个异步任务时,异步代码似乎部分阻塞(使用HttpClient),c#,asynchronous,async-await,task,blocking,C#,Asynchronous,Async Await,Task,Blocking,我一直在尝试学习C#与HttpClient的异步,但遇到了一个问题,我不知道发生了什么 我想做什么: 一次性发送多个HttpClient请求,并在响应返回时进行单独处理。此过程在循环中永远重复(即每隔几秒钟发送一批新的请求)。对结果进行的处理不是CPU密集型的,每个响应最多需要几毫秒 在任何情况下,数量为5-50个职位 不关心他们返回的顺序,但每个请求/响应必须在不阻塞的情况下尽可能快地处理 问题: 每当我一起发送一个请求列表时,响应时间似乎会稍微长一点,但它们应该大致相同。e、 g: 所

我一直在尝试学习C#与HttpClient的异步,但遇到了一个问题,我不知道发生了什么

我想做什么:

  • 一次性发送多个HttpClient请求,并在响应返回时进行单独处理。此过程在循环中永远重复(即每隔几秒钟发送一批新的请求)。对结果进行的处理不是CPU密集型的,每个响应最多需要几毫秒
  • 在任何情况下,数量为5-50个职位
  • 不关心他们返回的顺序,但每个请求/响应必须在不阻塞的情况下尽可能快地处理
问题:

  • 每当我一起发送一个请求列表时,响应时间似乎会稍微长一点,但它们应该大致相同。e、 g:

    所用时间:67.9609毫秒
    所用时间:89.2413毫秒
    所用时间:100.2345毫秒
    所用时间:117.7308毫秒
    所用时间:127.6843毫秒
    所用时间:132.3066毫秒
    所用时间:157.3057毫秒

当它们都应该在70毫秒左右时。。。每批新产品都会重复此操作;第一个很快,然后每个连续的一个都会变慢

我的代码
主要有三个部分。我已经尽可能地简化了它,以便集中精力解决问题发生的地方(Downloader.cs是我计时的)

  • Downloader.cs包含执行实际http查询的方法:

    public async Task<string> DownloadData(string requestString)
    {
        HttpResponseMessage response;
        response = await this.httpClient.PostAsync( url, new StringContent( requestString ));
        string returnString = await response.Content.ReadAsStringAsync();
        return returnString;
    }
    
  • Program.cs这只是一个控制台程序,在QueryAgent中的异步函数上调用一次任务,然后在最后调用console.Readline()以停止程序退出。我也在这里初始化HttpClient。我调用DownloadLoop()一次,如下所示:

    QueryAgent myAgent = new QueryAgent(httpClient);
    Task dataLoop = myAgent.DownloadLoopAsync();   
    Console.Readline();
    
我怀疑这与我在DownloadLoopAsync()中调用for循环中的新任务的方式有关,但是这没有意义,因为我假设每个新任务都将由自身处理

任何建议都是非常感谢的,因为我一直在一堵又一堵的砖墙,试图让它按预期工作

多谢各位

编辑1
只是在调用任务循环中搞得一团糟。我改变了:

foreach(string identifier in this.Identifiers)
{
    string newRequestString = this.generateRequestString(identifier );
    Task newRequest = RequestNewDataAsync(newRequestString);
}

现在,它可以按预期工作:

Time taken: 71.0088 milliseconds
Time taken: 65.0499 milliseconds
Time taken: 64.0955 milliseconds
Time taken: 64.4291 milliseconds
Time taken: 63.0841 milliseconds
Time taken: 64.9841 milliseconds
Time taken: 63.7261 milliseconds
Time taken: 66.451 milliseconds
Time taken: 62.4589 milliseconds
Time taken: 65.331 milliseconds
Time taken: 63.2761 milliseconds
Time taken: 69.7145 milliseconds
Time taken: 63.1238 milliseconds
现在,我更困惑于为什么这看起来是有效的

编辑2
Downloader.cs中的正时代码如下所示:

公共异步任务下载数据(字符串请求字符串)
{
HttpResponseMessage响应;
秒表s=Stopwatch.StartNew();
response=wait this.httpClient.PostAsync(url,newStringContent(requestString));
double timetake=s.eassed.total毫秒;
WriteLine(“所用时间:{0}毫秒”,timetake);
string returnString=await response.Content.ReadAsStringAsync();
返回字符串;
}
虽然等待任务。延迟(1);似乎是在“耍把戏”,这不是一个理想的解决方案,我也不明白它为什么会工作——也许它会给CPU时间初始化RequestNewDataAsync任务,并防止某种类型的并发锁定?我只能猜测。。。这还意味着它将循环限制为1000Hz,这是一个快速的限制,但也是一个限制,因为Task.Delay()只接受整数值,其中1是最小值

编辑3
多读一点书(我对这一切还比较陌生!),也许这是使用线程池的最佳选择(10到100个短期任务)?然而,这会产生过多的开销(每个新任务1MB?);或者这又是另一回事了

编辑4
在不同的地方做了更多的实验,延迟(1)

  • [减速发生]放置在Downloader.cs中httpClient.PostAsync()之前或之后
  • [减速发生]在RequestNewDataAsync()中的等待调用之前或之后放置
  • [无减速/预期性能]在DownloadLoopAsync()中的任务调用之前或之后放置

当您执行
wait
时,后续代码仅在您“等待”的内容完成后执行

要同时发送多个查询,您必须为每个查询创建新的
任务
,然后一起等待所有查询

var tasks = new List<Task>();

foreach (string identifier in Identifiers)
{
    var newRequestString = generateRequestString(identifier);
    var newRequest = RequestNewDataAsync(newRequestString);
    tasks.Add(newRequest);
}

Task.WhenAll(tasks); // synchronous wait for all tasks to complete
var tasks=newlist();
foreach(标识符中的字符串标识符)
{
var newRequestString=generateRequestString(标识符);
var newRequest=RequestNewDataAsync(newRequestString);
tasks.Add(newRequest);
}
任务。当所有(任务);//同步等待所有任务完成

最有可能的情况是,您的网络或远程服务器上充满了请求。您的无限循环不会在开始下一批之前等待上一批完成,因此随着时间的推移,您正在以超出其完成速度的速度发布批,从而导致积压

这里有一个修改过的循环,它在开始下一批之前等待一批完成。请注意,它不再等待
任务。延迟
,而是等待下载任务的集合

public async Task DownloadLoopAsync() 
{
    while ( this.isLooping )
    {
      var tasks = this
        .Identifiers
        .Select(id => RequestNewDataAsync(this.generateRequestString(id));

      await Task.WhenAll(tasks);
    }
}
注意,在存储处理后数据的结果时,存在竞争条件。这可能是您的问题,也可能不是:

public async Task RequestNewDataAsync(string requestString)
{
    string responseString = await this.downloader.DownloadData(requestString);

    // Note: you have a race condition here.
    // multiple downloads could complete simultaneously and try to concurrently
    // assign to ProcessedData.  This may or may not be an issue for you.
    this.ProcessedData = this.ProcessData(responseString);
}
此外,我还修改了主程序,以便在用户按键后等待最后一批完成:

QueryAgent myAgent = new QueryAgent(httpClient);
Task dataLoop = myAgent.DownloadLoopAsync();   
Console.Readline();
myAgent.isLooping = false;
dataLoop.Wait(); // let the last set of downloads complete

您将请求发送到哪里?您是否可能只是在加载服务器,因此响应较慢?
HttpClient
不能同时使用,它不是线程安全的。您也从不等待来自
RequestNewDataAsync
的结果任务,因此您会在较早的请求完成之前发送更多请求,给服务器带来越来越多的负载。您的方法似乎有点不标准。我和
public async Task DownloadLoopAsync() 
{
    while ( this.isLooping )
    {
      var tasks = this
        .Identifiers
        .Select(id => RequestNewDataAsync(this.generateRequestString(id));

      await Task.WhenAll(tasks);
    }
}
public async Task RequestNewDataAsync(string requestString)
{
    string responseString = await this.downloader.DownloadData(requestString);

    // Note: you have a race condition here.
    // multiple downloads could complete simultaneously and try to concurrently
    // assign to ProcessedData.  This may or may not be an issue for you.
    this.ProcessedData = this.ProcessData(responseString);
}
QueryAgent myAgent = new QueryAgent(httpClient);
Task dataLoop = myAgent.DownloadLoopAsync();   
Console.Readline();
myAgent.isLooping = false;
dataLoop.Wait(); // let the last set of downloads complete