【问题标题】:Async await how to use return values异步等待如何使用返回值
【发布时间】:2014-10-22 14:36:20
【问题描述】:

我有一个从另一个开发人员那里继承的 Windows 服务,它运行速度非常慢,并且对 eBay API 的调用很慢。我希望在不进行过多重构的情况下加快速度。

我刚刚开始研究使用 c# async/await 来尝试获取其中一些慢速调用来运行异步。 这是我想要实现的目标:

我有 1 个非常繁忙的方法,它会拨打很多电话,如下所示:

getProducts
getCategories
getVehicles
getImages

我的想法是,我可以简单地将方法更改为 async 并将 Task<T> 添加到返回类型,如下所示:

public async Task<String> ProcessAdditionalProductDetialsAsync(ItemType oItem)
{
    String additionalProductDetails = string.Empty;

    if (oItem.ItemSpecifics.Count > 0)
    {
        foreach (NameValueListType nvl in oItem.ItemSpecifics)
        {                  
            if (nvl.Value.Count > 0)
            {
                foreach (string s in nvl.Value)
                {
                    additionalProductDetails += "<li><strong>" + nvl.Name + ":</strong>&nbsp;" + s + "</li>";
                }
            }
        }
    }
    return additionalProductDetails;
}

然后用 await 调用它们:

Task<String> additionalProductDetials = ebayPartNumbers.ProcessAdditionalProductDetialsAsync(item);
Task<PartNumberCollection> partNumberCollection = ebayPartNumbers.ProcessPartNumbersAsync(item); 


await Task.WhenAll(partNumberCollection, additionalProductDetials);

如何获取返回的类型以便使用它们?我试过只使用partNumberCollection,但它只有await 可用的属性。

【问题讨论】:

  • 编译器应该警告你在ProcessAdditionalProductDetialsAsync 中使用async 而不使用await。这是您的实际代码还是真的在做await? eBay API 从何而来?
  • 是的,编译器确实警告该方法没有等待,但等待在父方法中。
  • 你应该注意编译器警告;如果是async在那个方法中应该有一个await。如果要在后台线程上执行代码,请使用Task.Run;如果你想做 I/O(即 eBay API),它应该使用自然异步方法。

标签: c# async-await


【解决方案1】:

在任务类上使用Result 属性:

await Task.WhenAll(partNumberCollection, additionalProductDetials);

var partNumberCollectionResult = partNumberCollection.Result;
var additionalProductDetialsResult = additionalProductDetials.Result;

【讨论】:

  • 您应该使用await 而不是Result 以避免将异常包装在AggregateException 中。
【解决方案2】:

如果Task.WhenAll返回的任务已经完成,那意味着你传递给它的所有任务也已经完成。这反过来意味着您可以使用每个任务的 Result 属性,而不会被阻塞。

string details = additionalProductDetials.Result;

或者,您可以等待任务,以与其他异步代码保持一致:

string details = await additionalProductDetials;

同样,这保证不会阻止 - 如果您稍后出于某种原因删除了 Task.WhenAll(例如,您很乐意在收集部件号之前使用详细信息开始另一项任务)然后您无需更改代码。

【讨论】:

    【解决方案3】:

    您的async 方法缺少await 运算符,将同步运行。当您调用非阻塞 API 时,您可以使用 Task.Run() 在后台线程上执行 cpu-bound 工作。

    public async Task<String> ProcessAdditionalProductDetialsAsync(ItemType oItem)
    {
        return await Task.Run(() =>
        {
            String additionalProductDetails = string.Empty;
    
            if (oItem.ItemSpecifics.Count > 0)
            {
                foreach (NameValueListType nvl in oItem.ItemSpecifics)
                {
                    if (nvl.Value.Count > 0)
                    {
                        foreach (string s in nvl.Value)
                        {
                            additionalProductDetails += "<li><strong>" + nvl.Name + ":</strong>&nbsp;" + s + "</li>";
                        }
                    }
                }
            }
            return additionalProductDetails;
        });
    }
    

    得到结果

    var detail = await ProcessAdditionalProductDetialsAsync(itemType);
    var result = ProcessAdditionalProductDetialsAsync(itemType).Result;
    

    【讨论】:

    • 调用this的父方法标有async,使用await Task.WhenAll,这不就是运行上面async的方法吗?
    【解决方案4】:

    试试这个代码:

    public async Task<String> ProcessAdditionalProductDetialsAsync(ItemType oItem) { 
        String additionalProductDetails = await Task.Run(() => {
           if (oItem.ItemSpecifics.Count > 0) {
              foreach (NameValueListType nvl in oItem.ItemSpecifics) { 
                 if (nvl.Value.Count > 0) {
                     string retval = String.Empty;
    
                     foreach (string s in nvl.Value) {
                         retval += "<li><strong>" 
                           + nvl.Name + ":</strong>&nbsp;" + s + "</li>";
                     }
                 }
              }
           }
           return retval;
         }
         return additionalProductDetails;
     }
    

    用法:

    private async void GetAdditionalProductDetailsAsync(Action<string> callback) {
       string apd = await ProcessAdditionalProductDetialsAsync();
       callback(apd);
    }
    
    private void AdditionalProductDetailsRetrieved(string apd) {
        // do anything with apd
    }
    

    【讨论】:

      猜你喜欢
      • 2020-03-13
      • 2021-12-13
      • 2017-09-24
      • 2018-01-03
      • 2013-02-14
      • 2020-08-19
      • 2013-07-24
      • 2019-03-20
      • 2015-08-05
      相关资源
      最近更新 更多