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> " + 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> " + 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> " + 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
}