Task.WhenAll vs multiple awaits - czy dobrze rozumiem?

0

cześc
pytanie jest następujące - czy dobrze rozumiem róznice


            var beforeDiff = await task1(before, after);
            var afterDiff = await task2(after, before);
//VS
 
            var totalTasks = Task.WhenAll(task1, task2);
 

W przypadku **multiple awaits **

  1. kod wykonuje się sekwencyjnie
  2. jest wiele zmian kontekstu (dla kolejnej metody await)
  3. mogę stracić wyjątki gdy poleciały we wczesniejszym await

w przypadku WhenAll

  1. kod wykonuje się równolegle
  2. nie ma pewności, który task będzie wykonany pierwszy - więc jeśli jeden zalezy od drugiego, to WhenAll nie jest najlepszym pomysłem
  3. jest jedna zmiana kontekstu
  4. nawet gdy będzie exception to nie zatrzyma to egzekucji tasków
0
jakubek napisał(a):
  1. jest wiele zmian kontekstu (dla kolejnej metody await)
    ...
  2. jest jedna zmiana kontekstu

O którym kontekście mówisz?

1
jakubek napisał(a):

W przypadku **multiple awaits **

  1. kod wykonuje się sekwencyjnie

Tak

jakubek napisał(a):
  1. jest wiele zmian kontekstu (dla kolejnej metody await)

Nie, tu nie ma żadnych zmian SynchronizationContext. Tu są potencjalnie dwie kontynuacje.

jakubek napisał(a):
  1. mogę stracić wyjątki gdy poleciały we wczesniejszym await

Nie i tak. Nie - masz await, więc wyjątki będą normalnie rzucone. Tak - await nie działa z wyjątkami z drzew zadań.

jakubek napisał(a):

w przypadku WhenAll

  1. kod wykonuje się równolegle

Prawdopodobnie, ale nie ma gwarancji.

jakubek napisał(a):
  1. nie ma pewności, który task będzie wykonany pierwszy - więc jeśli jeden zalezy od drugiego, to WhenAll nie jest najlepszym pomysłem

Tak.

jakubek napisał(a):
  1. jest jedna zmiana kontekstu

Znowu, nie ma żadnej zmiany kontekstu. Jest potencjalnie jedna kontynuacja.

jakubek napisał(a):
  1. nawet gdy będzie exception to nie zatrzyma to egzekucji tasków

Wyjątek w pierwszym zadaniu nie zatrzyma drugiego zadania. Wyjątek w pierwszym zadaniu zatrzyma dalsze wykonywanie pierwszego zadania.

1

Jeśli nie wiadomo - warto poeksperymentować.

jakubek napisał(a):

W przypadku **multiple awaits **

  1. kod wykonuje się sekwencyjnie
private static async Task Main(string[] args)
{
	await FirstTask();
	await SecondTask();

	Console.ReadKey();
}

private static async Task FirstTask()
{
	for (var i = 0; i < 5; i++)
	{
		Console.WriteLine($"From {nameof(FirstTask)}");
		await Task.Delay(1000);
	}
}

private static async Task SecondTask()
{
	for (var i = 0; i < 5; i++)
	{
		Console.WriteLine($"From {nameof(SecondTask)}");
		await Task.Delay(1000);
	}
}

Wynik:

From FirstTask
From FirstTask
From FirstTask
From FirstTask
From FirstTask
From SecondTask
From SecondTask
From SecondTask
From SecondTask
From SecondTask

w przypadku WhenAll

  1. kod wykonuje się równolegle
await Task.WhenAll(FirstTask(), SecondTask());

Wynik:

From FirstTask
From SecondTask
From SecondTask
From FirstTask
From SecondTask
From FirstTask
From FirstTask
From SecondTask
From FirstTask
From SecondTask

W przypadku **multiple awaits **
3) mogę stracić wyjątki gdy poleciały we wczesniejszym await

private static async Task Main(string[] args)
{
	await FirstTask();
	await SecondTask();

	Console.ReadKey();
}

private static async Task FirstTask()
{
	for (var i = 0; i < 5; i++)
	{
		Console.WriteLine($"From {nameof(FirstTask)}");
		await Task.Delay(100);

		if (i == 3)
		{
			throw new Exception("some exception");
		}
	}
}

private static async Task SecondTask()
{
	for (var i = 0; i < 5; i++)
	{
		Console.WriteLine($"From {nameof(SecondTask)}");
		await Task.Delay(100);
	}
}

Dostaję wyjątek (nieopakowany w AggregateException) natychmiast po jego wystąpieniu. Kończy wykonywanie programu.

w przypadku WhenAll
4) nawet gdy będzie exception to nie zatrzyma to egzekucji tasków

await Task.WhenAll(FirstTask(), SecondTask());

Dostaję wyjątek (nieopakowany w AggregateException) natychmiast po jego wystąpieniu. Kończy wykonywanie programu.

w przypadku WhenAll
2) nie ma pewności, który task będzie wykonany pierwszy - więc jeśli jeden zalezy od drugiego, to WhenAll nie jest najlepszym pomysłem

private static Random Rand = new Random(DateTime.Now.Millisecond);

private static async Task Main(string[] args)
{
	var t1 = FirstTask()
		.ContinueWith(t => Console.WriteLine("First completed"), TaskContinuationOptions.OnlyOnRanToCompletion);

	var t2 = SecondTask()
		.ContinueWith(t => Console.WriteLine("Second completed"), TaskContinuationOptions.OnlyOnRanToCompletion);

	await Task.WhenAll(t1, t2);

	Console.ReadKey();
}

private static async Task FirstTask()
{
	var time = Rand.Next(1, 5) * 1000;
	await Task.Delay(time);
}

private static async Task SecondTask()
{
	var time = Rand.Next(1, 5) * 1000;
	await Task.Delay(time);
}

Wynik:

Czasami First completed, Second completed, czasami na odwrót.

1 użytkowników online, w tym zalogowanych: 0, gości: 1