Я хотел бы запустить несколько методов asyncron в foreach. Возвращаемое значение должно быть записано в список.
Метод выполняется в приложении WPF. Метод GetItemPricesFromJsonAsync
извлекает данные из Интернета.
public async Task LoadBlackMarketListView(List<MarketAnalysisManager.ItemTier> tiers, List<MarketAnalysisManager.ItemLevel> levels,
List<MarketAnalysisManager.ItemQuality> quialityList, string outdatedHours, string profit, Location? location)
{
await Task.Run(async () =>
{
var blackMarketSellObjectList = new List<BlackMarketSellObject>();
var items = await MarketAnalysisManager.GetItemListAsync(tiers, levels);
await Dispatcher.InvokeAsync(() =>
{
PbBlackMarketMode.Minimum = 0;
PbBlackMarketMode.Maximum = items.Count;
PbBlackMarketMode.Value = 0;
GridBlackMarketMode.IsEnabled = false;
LvBlackMarket.Visibility = Visibility.Hidden;
PbBlackMarketMode.Visibility = Visibility.Visible;
});
foreach (var item in items)
{
var allItemPrices = await MarketAnalysisManager.GetItemPricesFromJsonAsync(item.UniqueName, true);
if (allItemPrices.FindAll(a => a.City == Locations.GetName(Location.BlackMarket)).Count <= 0)
{
await IncreaseBlackMarketProgressBar();
continue;
}
blackMarketSellObjectList.AddRange(await GetBlackMarketSellObjectList(item, quialityList, allItemPrices, outdatedHours, profit, location));
await IncreaseBlackMarketProgressBar();
}
await Dispatcher.InvokeAsync(() =>
{
LvBlackMarket.ItemsSource = blackMarketSellObjectList;
PbBlackMarketMode.Visibility = Visibility.Hidden;
LvBlackMarket.Visibility = Visibility.Visible;
GridBlackMarketMode.IsEnabled = true;
});
});
}
В настоящее время похоже, что он делает только одно дело за раз.
Беги... 0
Конец... 0
Беги... 1
Конец... 1
Беги... 2
Конец... 2
Да, это правда, но как мне сделать так, чтобы несколько вещей работали параллельно?
Если вы хотите продолжить использовать простой async/await, проверьте мой ответ. Если вы хотите использовать класс Parallel, проверьте этот ответ Михала.
Я только что заметил.. почему вы делаете await Task.Run(async () => ... в первой строке функции? Я не вижу в этом никакого преимущества, и это добавляет еще один уровень вложенности в функцию , Если вы этого не сделаете и поместите весь код непосредственно в функцию без анонимной промежуточной функции, вы должны получить то же самое, или я что-то упустил?
Это правильно @Joelius. Спасибо за подсказку.
Вам нужно будет хранить Задачи, а не ждать их. Тогда вы можете дождаться их всех.
Попробуйте это (замените свой foreach моим кодом).
Я бы также посоветовал вам использовать реальный метод вместо анонимного, он намного читабельнее.
List<Task> tasks = new List<Task>();
foreach (var item in items)
{
tasks.Add(Task.Run(async () =>
{
var allItemPrices = await MarketAnalysisManager.GetItemPricesFromJsonAsync(item.UniqueName, true);
if (allItemPrices.FindAll(a => a.City == Locations.GetName(Location.BlackMarket)).Count <= 0)
{
await IncreaseBlackMarketProgressBar();
return;
}
blackMarketSellObjectList.AddRange(await GetBlackMarketSellObjectList(item, quialityList, allItemPrices, outdatedHours, profit, location));
await IncreaseBlackMarketProgressBar();
}));
}
await Task.WhenAll(tasks);
Примечание. Теперь вместо продолжения используется возврат, так как это анонимная функция, и вам просто нужно завершить функцию там, а не продолжать foreach.
Это прекрасно работает. К сожалению, время загрузки не меньше. Так что для пользователя это мало что меняет.
@Triky313 Если у вас слишком много элементов, это может даже замедлить работу программы.
@ Triky313 Triky313 Если это не ускорит процесс, могут быть какие-то другие зависимости, или это займет очень мало времени. Я протестировал его с использованием массива int в качестве элементов, превратил ожидания в простые задержки (Task.Delay) и вернулся, когда int был нечетным. Мой способ немного ускорил код по сравнению с вашим первоначальным способом.
@Rekshino, не могли бы вы рассказать об этом подробнее? Звучит очень интересно, и я не совсем понимаю, что вы имеете в виду.
@Joelius См., например, Parallel.ForEach против Task.Factory.StartNew
@ Рекшино, ааа, понятно. Я использую класс Parallel только для очень большого количества элементов, и, исходя из моего опыта, чаще всего этот порог не достигается. Я думаю, вам придется провести некоторые измерения, чтобы определить, какой из них работает лучше в этом случае. Я думаю, любой из них будет работать нормально.
@Joelius Хорошо, это немного быстрее, ты прав. Этот метод «MarketAnalysisManager.GetItemPricesFromJsonAsync (item.UniqueName, true)» занимает много времени, поскольку данные загружаются из Интернета. Нельзя ли распараллелить эти 1000 запросов... или так происходит с Task.Run()?
Что происходит, так это то, что он начинает выполнение, но затем уже запускает следующий, не дожидаясь завершения первого. После foreach мы ждем завершения всех задач (в настоящее время они выполняются параллельно (проверьте определение параллельности при использовании задач, если вам нужна дополнительная информация об этом)).
Если вы считаете, что этот метод недостаточно быстр, вы можете попробовать заставить его работать с классом Parallel. По моему опыту, случаев (в случайном программировании) меньше, когда класс Parallel превосходит мой метод. Не стесняйтесь попробовать другой метод, потому что иногда он дает вам удивительно большой прирост производительности.
@Joelius Разве нельзя повторно использовать задачу?
await приостанавливает выполнение до тех пор, пока ожидаемая задача не завершится, тем самым заставляя foreach выполняться синхронно.