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):

2) jest wiele zmian kontekstu (dla kolejnej metody await)
...
3) 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):

2) jest wiele zmian kontekstu (dla kolejnej metody await)

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

jakubek napisał(a):

3) 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):

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

Tak.

jakubek napisał(a):

3) jest jedna zmiana kontekstu

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

jakubek napisał(a):

4) 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, botów: 0