Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why awaiting cold Task does not throw

I was just experimenting to see what happens when a cold task (i.e. a Task which hasn't been started) is awaited. To my surprise the code just hung forever and "Finsihed" is never printed. I would expect that an exception is thrown.

public async Task Test1()
{
    var task = new Task(() => Thread.Sleep(1000));
    //task.Start();
    await task;
}

void Main()
{
    Test1().Wait();
    Console.WriteLine("Finished");
}

Then I though perhaps the task can be started from another thread, so I changed the code to:

public async Task Test1()
{
    var task = new Task(() => Thread.Sleep(1000));
    //task.Start();
    await task;

    Console.WriteLine("Test1 Finished");
}

void Main()
{
    var task1 = Test1();

    Task.Run(() => 
    {
        Task.Delay(5000);   
        task1.Start();
    });

    task1.Wait();
    Console.WriteLine("Finished");
}

But it is still blocked at task1.Wait(). Does anyone know if there is way to start a cold task after it has being awaited?

Otherwise it seems there is no point in being able to await a cold task, so perhaps the task should either be started when awaited or an exception should be thrown.

Update

I was awaiting the wrong task, i.e. the outer task returned by Test1 rather than the one newed inside it. The InvalidOperationException mentioned by @Jon Skeet was being thrown inside Task.Run however because the resulting task was not observed, the exception was not thrown on the main thread. Putting a try/catch inside Task.Run or calling Wait() or Result on the task returned by Task.Run threw the exception on the main console thread.

like image 500
NeddySpaghetti Avatar asked Jun 16 '14 05:06

NeddySpaghetti


People also ask

What happens when you don't await a task?

If you don't await the task or explicitly check for exceptions, the exception is lost. If you await the task, its exception is rethrown. As a best practice, you should always await the call. By default, this message is a warning.

Is await blocking C#?

The await keyword, by contrast, is non-blocking, which means the current thread is free to do other things during the wait.

Does task wait start task?

Wait is a synchronization method that causes the calling thread to wait until the current task has completed. If the current task has not started execution, the Wait method attempts to remove the task from the scheduler and execute it inline on the current thread.

Why we use async and await in asp net core?

When we don't want to return a result from our async method, we should always return a Task. To validate our asynchronous operations, we have to use the await keyword while calling that operation. When we convert our synchronous code to asynchronous, we have to use the async and await keywords all the way up the chain.


2 Answers

You're trying to start the task returned by the async method - that isn't the cold task that you started out with. Indeed, if you add some diagnostics to your Task.Run call, you'll see that an exception is thrown:

System.InvalidOperationException: Start may not be called on a promise-style task.

Here's an example showing what I think you were really trying to do:

using System;
using System.Threading;
using System.Threading.Tasks;

public class Test
{
    static void Main(string[] args)
    {
        // Not using Task.Delay! That would be pointless
        Task t1 = new Task(() => Thread.Sleep(1000));
        Task t2 = Await(t1);
        Console.WriteLine(t2.Status);
        Console.WriteLine("Starting original task");
        t1.Start(); 
        Console.WriteLine(t2.Status);
        t2.Wait();
        Console.WriteLine(t2.Status);        
    }

    static async Task Await(Task task)
    {
        Console.WriteLine("Beginning awaiting");
        await task;
        Console.WriteLine("Finished awaiting");        
    }
}

Note the use of Thread.Sleep instead of Task.Delay; unless you're using the result of Task.Delay, it basically does nothing. Using Thread.Sleep is emulating real work to be done in the other task.

As for why awaiting an unstarted task doesn't throw an exception - I think that's reasonable, to be honest. It allows for situations like the above to be valid, which may in some cases make life easier. (You may create a lot of tasks before starting them, for example - and you may want to start waiting for them to finish before you start them.)

like image 153
Jon Skeet Avatar answered Sep 26 '22 00:09

Jon Skeet


Does anyone know if there is way to start a cold task after it has being awaited?

You still can create a cold task from an async method and start it later, if that's what you want:

class Program
{
    public static async Task Test1()
    {
        await Task.Delay(1000);
        Console.WriteLine("Test1 is about to finish");
    }

    static void Main(string[] args)
    {
        var taskOuter = new Task<Task>(Test1);
        var taskInner = taskOuter.Unwrap();

        Task.Run(() =>
        {
            Thread.Sleep(2000);

            // run synchronously
            taskOuter.RunSynchronously();

            // or schedule
            // taskOuter.Start(TaskScheduler.Defaut);
        });

        taskInner.Wait();
        Console.WriteLine("Enter to exit");
        Console.ReadLine();
    }
}
like image 44
noseratio Avatar answered Sep 24 '22 00:09

noseratio