【问题标题】:Asynchronous method using .Result使用 .Result 的异步方法
【发布时间】:2015-11-28 11:14:31
【问题描述】:

以下方法循环通过 postdata 列表以对令牌发出多个请求,例如每个请求使用特定的 clientID。我的问题与异步有关。我正在尝试对令牌进行异步调用。 .Result 的使用是否必然使方法同步?

public async Task<string> ReturnDataFromUrl1(List<List<KeyValuePair<string, string>>> listOfPostData)
{        
   List<Task<string>> taskList = new List<Task<string>>();       
   string allTokens = "";
   List<Task<string>> downloadTasks = new List<Task<string>>();


   foreach (var postData in listOfPostData)
   {
       using (var client = new HttpClient())
       {
           client.BaseAddress = new Uri("http://localhost:23081");

           HttpContent httpContent = new FormUrlEncodedContent(postData);
           HttpResponseMessage response = client.PostAsync("/Token", httpContent).Result;

           var responsecode = (int)response.StatusCode;

           if (response.IsSuccessStatusCode)
           {
               var responseBodyAsText = response.Content.ReadAsStringAsync();

               taskList.Add(responseBodyAsText);
           }
       }
   }

    downloadTasks = taskList.ToList();            

    while (downloadTasks.Count > 0)
    {
        Task<string> firstFinishedTask = await Task.WhenAny(downloadTasks);

        downloadTasks.Remove(firstFinishedTask);

        // Await the completed task. 
        string content = await firstFinishedTask;

        allTokens = allTokens + content;
    }

    return allTokens;
}

【问题讨论】:

    标签: c# asynchronous async-await task-parallel-library httpclient


    【解决方案1】:

    .Result 的使用是否必然使方法同步?

    它会使.Result同步的部分,因为它同步等待Task的完成(如果你的代码没有死锁,这很有可能在带有自定义 SynchronizationContext 的环境)。

    这个方法调用:

    Task<string> firstFinishedTask = await Task.WhenAny(downloadTasks);
    

    将是异步的,因为它将产生控制权,直到第一个流被读取并转换为字符串。 如果您已经有一个标记为async 的方法,只需await 该部分也是如此:

    HttpResponseMessage response = await client.PostAsync("/Token", httpContent);
    

    旁注:

    我想我会采取不同的方法来解决这个问题。一般来说,这种方法最耗时的是网络 IO。如果可能并且没有限制,我会同时进行这些网络调用,然后在结果出现时对其进行处理:

    public async Task<string> ReturnDataFromUrlAsync(
                                    List<List<KeyValuePair<string, string>>> listOfPostData)
    {
        var client = new HttpClient
        {
            BaseAddress = new Uri("http://localhost:23081")
        };
    
        var downloadTasks = listOfPostData.Select(postData =>
        {
            var content = new FormUrlEncodedContent(postData);
            return client.PostAsync("/Token", content);
        }).ToList();
    
        var tokenBuilder = new StringBuilder(downloadTasks.Count);
        while (downloadTasks.Count > 0)
        {
            var finishedTask = await Task.WhenAny(downloadTasks);
            downloadTasks.Remove(finishedTask);
            var response = await finishedTask;
    
            if (!response.IsSuccessStatusCode)
                continue;
    
            var token = await response.Content.ReadAsStringAsync();
            tokenBuilder.Append(token);
        }
    
        return tokenBuilder.ToString();
    }
    

    或者由于您需要所有结果才能处理令牌,您可以使用Task.WhenAll 等待所有结果完成:

    public async Task<string> ReturnDataFromUrlAsync(
                                    List<List<KeyValuePair<string, string>>> listOfPostData)
    {
        var client = new HttpClient
        {
            BaseAddress = new Uri("http://localhost:23081")
        };
    
        var downloadTasks = listOfPostData.Select(postData =>
        {
            var content = new FormUrlEncodedContent(postData);
            return client.PostAsync("/Token", content);
        });
    
        HttpResponseMessage[] response = await Task.WhenAll(downloadTasks);
    
        var tokenBuilder = new StringBuilder(response.Length);
        foreach (var element in response.Where(message => message.IsSuccessStatusCode))
        {
            tokenBuilder.Append(await element.Content.ReadAsStringAsync());
        }
        return tokenBuilder.ToString();
    }
    

    【讨论】:

    • 嗨@Yuval,你一针见血。我不确定如何等待 client.PostAsync("/Token",httpContent);因为它需要一些重构。比如我需要给taskList.Add返回一个任务,而不是一个字符串
    • @Steve 查看我的编辑,了解如何解决此问题的类似但略有不同的方法。
    • 方法在 var finishedTask = await Task.WhenAny(downloadTasks);我正在调试它
    • 没看到你后面的帖子,去看看
    • @Steve 出炸弹是什么意思?例外?
    猜你喜欢
    • 2016-12-05
    • 2019-12-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多