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.
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.
The await keyword, by contrast, is non-blocking, which means the current thread is free to do other things during the wait.
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.
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.
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.)
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();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With