【问题标题】:How to simplify multiple awaits with a single 'await Task.WhenAll'?如何使用单个“await Task.WhenAll”简化多个等待?
【发布时间】:2018-01-11 20:52:43
【问题描述】:

我假设我必须在下面的代码中使用Task.WhenAll,但无法弄清楚它应该正确实现。

请帮忙。

 public async void UpdateData()
        {
            var month = (cbMonths.SelectedItem as MonthView).ID;
            var year = (cbYears.SelectedItem as YearView).ID;
            var deviceTypeID = (int)DeviceType;
            var calendar = await GetCalendar(month, year, deviceTypeID);
            var workTypes = await GetWorkTypes(); 

            if (calendar != null && workTypes != null) // Task.WhenAll ???
            {
                //...
            }
        }


 private async Task<List<WorkTypeItem>> GetWorkTypes()
        {
            try
            {
                HttpClient client = new HttpClient();

                var url = Properties.Settings.Default.ServerBaseUrl + @"/api/staff/WorkTypes";

                HttpResponseMessage response = await client.GetAsync(url);
                if (response.IsSuccessStatusCode)    // Check the response StatusCode
                {
                    var serSettings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All };    
                    string responseBody = await response.Content.ReadAsStringAsync();

                    return JsonConvert.DeserializeObject<List<MSOCommon.WorkTypeItem>>(responseBody, serSettings); 
                }              
                else
                {
                    logger.Error(Properties.Resources.DATACannotGetWorkTypes);
                }
            }
            catch (Exception ex)
            {
                logger.Error(Properties.Resources.DATACannotGetWorkTypes + " " + ex.Message);
            }

            return null;
        }

【问题讨论】:

  • @Dimi 不要等待方法。将他们的任务传递给变量并在Task.WhenAll 中调用它们
  • 对不起,如果我说话天真,但为什么上面的代码不起作用?由于方法是async,并且在每个异步调用上都使用await,所以if (calendar != null &amp;&amp; workTypes != null) 只会在前两个语句完成后执行。不是吗?
  • 不,我不认为有什么问题。当您有很多电话要拨打时,Task.WhenAll 是另一种选择。请参阅docs 中的这一行:“await 运算符应用于异步方法中的任务,以在方法的执行中插入暂停点,直到等待的任务完成。”
  • 代码可以以任何方式工作。不同之处在于它们的执行方式。在您的 OP 中,每个任务将一个接一个地执行。使用Task.WhenAll,它们同时执行。
  • @crazyGamer 你是对的

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


【解决方案1】:
var calendarTask = GetCalendar(month, year, deviceTypeID);
var workTypesTask = GetWorkTypes(); 

Task.WaitAll(calendarTask, workTypesTask);
var calendar = await calendarTask;
var workTypes = await workTypesTask;

回答@crazyGamer,你这样做的原因是两个任务可以同时运行。否则,您甚至在开始第二个任务之前都在等待第一个任务。当然,如果他们相互依赖,那是一件好事。否则,这往往会在 MP 系统上运行得更快。

【讨论】:

  • 你不应该等待两次,你可以有效地使用这段代码。在这种情况下不需要 WaitAll。要么,要么“放弃”等待,并改为访问.Result
  • @JohnySkovdal - 来自 .Result 上的 msdn:“访问属性的 get 访问器会阻塞调用线程,直到异步操作完成;它相当于调用 Wait 方法”(可以肯定的是,在使用.Result这一点更清楚,但我不喜欢有副作用的属性,所以一般避免)
  • 我的意思很简单,您已经在等待 Task.WaitAll,如果您在相同任务上再次使用 await,不妨跳过该行?
【解决方案2】:

如果您希望两个任务同时执行,请不要使用await 方法。而是将他们的任务传递给变量并在Task.WhenAll中调用它们

public async Task UpdateData() {
    var month = (cbMonths.SelectedItem as MonthView).ID;
    var year = (cbYears.SelectedItem as YearView).ID;
    var deviceTypeID = (int)DeviceType;
    var task1 = GetCalendar(month, year, deviceTypeID);
    var task2 = GetWorkTypes(); 

    await Task.WhenAll(task1, task2);

    var calendar = task1.Result;
    var workTypes = task2.Result;
}

还请注意,您应该避免使用async void 方法。

【讨论】:

  • +1 指出async void。最好返回 Task 对象,因为在调用它时您可以更好地控制执行。
猜你喜欢
  • 2013-08-21
  • 2012-11-06
  • 2023-03-26
  • 1970-01-01
  • 2018-04-03
  • 2016-08-22
  • 1970-01-01
  • 2018-05-11
相关资源
最近更新 更多