Async ждет, как использовать возвращаемые значения

У меня есть служба Windows, которую я унаследовал от другого разработчика, она работает очень медленно и имеет множество медленных вызовов API eBay. Я хочу ускорить его, не слишком много рефакторинга.

Я только начал смотреть на использование С# async/await, чтобы попытаться получить некоторые из этих медленных вызовов для запуска async. Вот чего я пытаюсь достичь:

У меня есть один очень загруженный метод, который вызывает множество вызовов, как показано ниже:

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;
}

Затем вызовите их с ожиданием:

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


await Task.WhenAll(partNumberCollection, additionalProductDetials);

Как мне получить возвращаемые типы, чтобы я мог их использовать? Я попытался использовать partNumberCollection, но он имеет только доступные свойства await.

Ответы

Ответ 1

Используйте свойство Результат в классе Task:

await Task.WhenAll(partNumberCollection, additionalProductDetials);

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

Ответ 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;

Ответ 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
}