Korzystam z klienta API, który jest całkowicie asynchroniczny, to znaczy każda operacja albo zwraca, Task
albo Task<T>
np .:
static async Task DoSomething(int siteId, int postId, IBlogClient client)
{
await client.DeletePost(siteId, postId); // call API client
Console.WriteLine("Deleted post {0}.", siteId);
}
Używając operatorów asynchronicznych / oczekujących C # 5, jaki jest właściwy / najbardziej wydajny sposób uruchamiania wielu zadań i czekania na ich zakończenie:
int[] ids = new[] { 1, 2, 3, 4, 5 };
Parallel.ForEach(ids, i => DoSomething(1, i, blogClient).Wait());
lub:
int[] ids = new[] { 1, 2, 3, 4, 5 };
Task.WaitAll(ids.Select(i => DoSomething(1, i, blogClient)).ToArray());
Ponieważ klient API używa HttpClient wewnętrznie, spodziewam się, że natychmiast wyda 5 żądań HTTP, pisząc do konsoli po zakończeniu każdego z nich.
c#
.net
task-parallel-library
async-await
c#-5.0
Ben Foster
źródło
źródło
Odpowiedzi:
Chociaż operacje są uruchamiane równolegle z powyższym kodem, kod ten blokuje każdy wątek, na którym działa każda operacja. Na przykład, jeśli połączenie sieciowe trwa 2 sekundy, każdy wątek zawiesza się przez 2 sekundy bez robienia czegokolwiek poza czekaniem.
Z drugiej strony powyższy kod z
WaitAll
blokuje również wątki, a twoje wątki nie będą mogły przetwarzać żadnej innej pracy do czasu zakończenia operacji.Zalecane podejście
Wolałbym, aby
WhenAll
Twoje operacje były wykonywane asynchronicznie równolegle.Aby to zrobić, oto szczegółowy post na blogu, w którym omówiono wszystkie alternatywy oraz ich zalety / wady: jak i gdzie współbieżne asynchroniczne operacje we / wy z interfejsem API sieci Web ASP.NET
źródło
WaitAll
blokuje również wątki” - czy nie blokuje tylko jednego wątku, tego, który wywołałWaitAll
?Byłem ciekawy wyników metod podanych w pytaniu, a także zaakceptowanej odpowiedzi, więc poddałem je próbie.
Oto kod:
I wynikowy wynik:
źródło
Ponieważ wywoływany interfejs API jest asynchroniczny,
Parallel.ForEach
wersja nie ma większego sensu. Nie powinieneś używać.Wait
tejWaitAll
wersji, ponieważ straciłoby to równoległość. Inna alternatywa, jeśli wywołujący używa asynchronii, korzystaTask.WhenAll
po wykonaniuSelect
iToArray
wygenerowaniu tablicy zadań. Drugą alternatywą jest użycie Rx 2.0źródło
Możesz użyć
Task.WhenAll
funkcji, którą możesz przekazać n zadań;Task.WhenAll
zwróci zadanie, które zostanie ukończone, gdy wszystkie zadania, które przekazałeś doTask.WhenAll
wykonania. Musisz czekać asynchronicznieTask.WhenAll
, aby nie zablokować wątku interfejsu użytkownika:źródło
Parallel.ForEach
wymaga listy pracowników zdefiniowanych przez użytkownika i nie asynchronizacjiAction
z każdym pracownikiem.Task.WaitAll
iTask.WhenAll
wymagająList<Task>
, które z definicji są asynchroniczne.Znalazłem RiaanDP „s odpowiedź bardzo przydatna, aby zrozumieć różnicę, ale to wymaga korekty o
Parallel.ForEach
. Brak wystarczającej reputacji, aby odpowiedzieć na jego komentarz, a więc moja własna odpowiedź.Wynikowy wynik jest poniżej. Czasy wykonania są porównywalne. Przeprowadziłem ten test, gdy mój komputer wykonywał cotygodniowe skanowanie antywirusowe. Zmiana kolejności testów zmieniła czasy ich wykonywania.
źródło