Search code examples
c#.nettasktask-parallel-library

.NET TPL: Why is delegate being ran like it was executed with blocking wait call?


When I'm trying to run Task.Run method with delegate which runs another task without blocking call it's being ran like my task was exectued with Wait blocking call?

Here is an example: I'm trying to run this:

namespace Console_App_Sandbox
{
    internal class Program
    {
        static async Task Main(string[] args)
        {
            CancellationTokenSource cts = new CancellationTokenSource(2000);
            try
            {
                await Task.Run(() => Task.Delay(3000, cts.Token), cts.Token);
                //Task.Delay(3000, cts.Token).Wait();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
            Console.WriteLine("Hello, World!");
        }
    }
}

And I'm getting an exception.

But in case If I'd ran it not via Task.Run I'd not get an exception because other not awaited/waited thread's cancelled task exception won't be caught in the current thread:

namespace Console_App_Sandbox
{
    internal class Program
    {
        static void Main(string[] args)
        {
            CancellationTokenSource cts = new CancellationTokenSource(2000);
            try
            {
                //await Task.Run(() => Task.Delay(3000, cts.Token), cts.Token);
                Task.Delay(3000, cts.Token);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
            Console.WriteLine("Hello, World!");
        }
    }
}

Adding the blocking wait call will get us to the exception caughting:

namespace Console_App_Sandbox
{
    internal class Program
    {
        static void Main(string[] args)
        {
            CancellationTokenSource cts = new CancellationTokenSource(2000);
            try
            {
                //await Task.Run(() => Task.Delay(3000, cts.Token), cts.Token);
                Task.Delay(3000, cts.Token).Wait();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
            Console.WriteLine("Hello, World!");
        }
    }
}

When I've added async keyword to the delegate I'm passing into the Task.Run method exception won't be caught:

using System.Runtime.CompilerServices;

namespace Console_App_Sandbox
{
    internal class Program
    {
        static async Task Main(string[] args)
        {
            CancellationTokenSource cts = new CancellationTokenSource(2000);
            try
            {
                await Task.Run( async () => Task.Delay(3000, cts.Token), cts.Token);
                //Task.Delay(3000, cts.Token).Wait();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
            Console.WriteLine("Hello, World!");
        }
    }
}

How does it work? It seems like when delegate is not async blocking wait call will be added to tasks which were ran.


Solution

  • I've noticed that I've passed Func instead of Action and I couldn't understand that I've returned an inner task from the Task.Run method. Now it's pretty logical that exception is caught. Thanks much for all who helped my to find out the reason of such behaviour, especially for Panagiotis Canavos!