Warum dauert dieser C# Task so lange?

Was ist beim Mischen von synchronen mit asynchronen Tasks in C# das Problem, sodass der folgende Code extrem lange zur Ausführung benbötigt?

var sema = new SemaphoreSlim(1);

var tasks = new List<Task>();

for (int i = 0; i < 20; i++)
{
    var t2 = Task.Run(async () =>
    {
        var sw = new Stopwatch();
        sw.Start();
        await sema.WaitAsync().ConfigureAwait(false);
        try
        {
            await Task.Delay(1).ConfigureAwait(false);
        }
        finally
        {
            sema.Release();
        }
        sw.Stop();
        Console.WriteLine($"sync {sw.Elapsed}");
    });

    var t1 = Task.Run(() =>
    {
        var sw = new Stopwatch();
        sw.Start();
        sema.Wait();
        try
        {
        }
        finally
        {
            sema.Release();
        }
        sw.Stop();
        Console.WriteLine($"async {sw.Elapsed}");
    });

    tasks.Add(t1);
    tasks.Add(t2);
}

await Task.WhenAll(tasks).ConfigureAwait(false);

Ausgabe: es dauert 16 Sekunden!!! Nur die erste Ausgabe ist schnell, der Rest nach 16 Sekunden.

sync 00:00:00.8306484
sync 00:00:16.8401071
... Rest

Nehme ich beide male synchrone Tasks, geht es wesentlich schneller:

var sema = new SemaphoreSlim(1);

var tasks = new List<Task>();

for (int i = 0; i < 20; i++)
{
    var t2 = Task.Run(async () =>
    {
        var sw = new Stopwatch();
        sw.Start();
        await sema.WaitAsync().ConfigureAwait(false);
        try
        {
            await Task.Delay(1).ConfigureAwait(false);
        }
        finally
        {
            sema.Release();
        }
        sw.Stop();
        Console.WriteLine($"sync {sw.Elapsed}");
    });

    var t1 = Task.Run(async () =>
    {
        var sw = new Stopwatch();
        sw.Start();
        await sema.WaitAsync().ConfigureAwait(false);
        try
        {
        }
        finally
        {
            sema.Release();
        }
        sw.Stop();
        Console.WriteLine($"async {sw.Elapsed}");
    });

    tasks.Add(t1);
    tasks.Add(t2);
}

await Task.WhenAll(tasks).ConfigureAwait(false);

Ausgabe: alles nach etwa 300ms fertig.

async 00:00:00.0180861
sync 00:00:00.3329542
...

Was passiert da im Hintergrund? Ich weiß, dass man snchrone mit asynchronen Tasks nicht mischen soll, aber die 16 Sekunden sind schon heftig und kommen fast einem Deadlock gleich. Was passiert nach 16 Sekunden, dass es dann plötzlich weitergeht ;-) ?

programmieren, C Sharp, Informatik

Meistgelesene Beiträge zum Thema C Sharp