I'm trying to get my head around await and async so I wrote this little test app, but what I expected does not happen.
Instead of waiting for the task to complete, the program continues to execute.
class Program
{
static void Main(string[] args)
{
var task = new Task(Run);
task.Start();
task.Wait();
Console.WriteLine("Main finished");
Console.ReadLine();
}
public async static void Run()
{
var task = Task.Factory.StartNew(() =>
{
Console.WriteLine("Starting");
Thread.Sleep(1000);
Console.WriteLine("End");
});
await task;
Console.WriteLine("Run finished");
}
}
Output
Main finished
Starting
End
Run finished
If I swap the 'await task' for 'task.Await()' it then runs as I would have expected producing
Starting
End
Run finished
Main finished
That is because when you have asynchronous void method, there is nothing you can do to track it's completion. Yours new Task(Run) only creates a task for starting the Run method. After the Run arrives at first await, there is nothing tracking the progress of the method, because there is nothing associated with the progress of the method. To fix it, you have to return Task instead of void and await that, instead of creating new Task.
Difference between async void and async Task is described here.
Related
I create a Task variable and try to await it just for learning purposes with the next code:
static async Task Main(string[] args)
{
Console.WriteLine("Start");
Task myTask = new Task(() => { Console.WriteLine("Done"); });
await myTask;
Console.WriteLine("Finish");
}
Application writes Start in the console and then it freezes. I am not sure how to understand whats happened here and why does it freeze. What can be the reason?
I know that usually we apply await to the methods which return Task, but not variable. But vs compiles such code successfully. The expectation was to get all 3 messages in the console.
new Task() creates a new task instance, but does not start it.
so your task never runs, and never has a chance to finish. to fix it you, have to start it first:
static async Task Main(string[] args)
{
Console.WriteLine("Start");
Task myTask = new Task(() => { Console.WriteLine("Done"); });
myTask.Start();
await myTask;
Console.WriteLine("Finish");
}
i also recommend reading Microsofts Tutorial on tasks
Do not ever use the Task constructor (link is to my blog). There are literally zero use cases for it that aren't achieved by better means.
If you want to start work running on the thread pool, then use Task.Run:
static async Task Main(string[] args)
{
Console.WriteLine("Start");
Task myTask = Task.Run(() => { Console.WriteLine("Done"); });
await myTask;
Console.WriteLine("Finish");
}
I want to pause the program so it does not close. I have a code running in the main thread. Tried this way, but this code instantly skips and the program closes instead of waiting.
How can I make it wait for the WaitMy method to complete?
static void Main(string[] args)
{
//any code here...
Console.WriteLine("Discovery started");
WaitMy();
}
private async static void WaitMy()
{
//await Task.Delay(30 * 1000);
await Task.Run(async () => await Task.Delay(30 * 1000));
}
The application runs with .net 4.5.
Change the code to following to make it work:
static async Task Main(string[] args)
{
//any code here...
Console.WriteLine("Discovery started");
await WaitMy();
}
How this works ?
You need C# 7.1 or later versions
Now Main method can be declared async and has return type as Task for introducing await
It will simply let the delay execute asynchronously, but will renter the main context for continuation, which will not exit
Another suggestion would be, you just need await Task.Delay(30 * 1000), wrapping inside the Task.Run is not required here
You are using Task Programming Library. What happens here is that, in your WaitMy method, you are scheduling a task to be executed (await Task.Run(...)). The task is being executed on a background thread.
In the meanwhile, the Main method continues its execution after the call to the WaitMy method.
The Main method ends right after, so the foreground thread is terminated, and all the background threads associated to its process are terminated too.
If you just want to perform a delay, just use:
System.Threading.Thread.Sleep(30 * 1000);
in the Main method, instead of calling WaitMy.
The method WaitMy can return a Task instance so you can have your main thread wait for it to be completed.
static void Main(string[] args)
{
//any code here...
Console.WriteLine("Discovery started");
var task = WaitMy();
task.Wait();
Console.WriteLine("And done :)");
}
private static Task WaitMy()
{
//await Task.Delay(30 * 1000);
return Task.Run(async () => await Task.Delay(30 * 1000));
}
If you just want the program to wait before terminating a simple answer would be to use
Console.ReadKey();
If you are curious why program terminates without waiting it is because you delegate waiting to ThreadPool by using Task.Run so waiting occurs on another thread an your application simply terminates before it can receive any result from ThreadPool
Trying to understand async/await in the sample below:
public static Task <string> job()
{
Console.WriteLine("start delay 1");
Task.Delay(2000);
Console.WriteLine("sleep done 1");
Task<string> t = new Task<string>(()=> {
Console.WriteLine("start delay 2");
Task.Delay(3000);
Console.WriteLine("sleep done 2");
return "aaaa"; }) ;
t.Start();
return t;
}
public async static void caller()
{
string s = await job();
Console.WriteLine("result is {0}", s);
}
public static void Main()
{
caller();
Console.WriteLine("done with caller");
Console.ReadLine();
}
To get a more clear picture I would like to make task run a bit longer and I have added Task.Delay(). Both delays are taking no time and finish immediately. Why? How to make my task spend some time on simple job to make it last longer?
Task.Delay creates a Task that delays for the time specified. You do spawn a Task that Delays for 2 Seconds but you never wait for it.
adding await to the call does fix this. (You have to mark the method async)
public static async Task <string> job()
{
...
await Task.Delay(2000);
...
}
You can read more about Asynchronous Programming here
Task.Delay returns you a task that will complete in the given milliseconds you have provided. To hold execution of your code at this point (this is a massive simplification of what it does and you should definately do more learning on async/await in c#) you need to await the call to Task.Delay(2000) like this
await Task.Delay(2000);
to use the await key word you need to mark your method signature as async like this
public static async Task<string> job()
but I would highly recommend reading up on async and await
I am learning task aysny based programing and cannot get to make this code work. The console prints the message only once and then disappears.
if I remove the read line and run the program(not debug mode) the console just appears with message saying press a key to continue. When I debug and put the debugger in console.write then it works fine for some time and then the console window disappears and restarts again. If I use for loop <10000 instead of while then also the behaviors is same
Could you please suggest what I am doing wrong.
static void Main(string[] args)
{
multitasker();
}
static async void multitasker()
{
Task task1 = new Task(PrintMessageA);
task1.Start();
await task1;
}
static void PrintMessageA()
{
while(true)
{
Console.WriteLine("Message from A");
Console.ReadLine();
}
}
Your main thread does not block and thus is exiting immediately. You would have to go "await all the way" in a sense and await multitasker as well, but you can't actually do that as seen later.
So first you return a task in multitasker
static async Task multitasker()
{
Task task1 = new Task(PrintMessageA);
task1.Start();
await task1;
}
The problem is you cannot make Main() (the entry point) async, so instead you would need to block that thread by instead calling Wait() on the returned Task
static void Main(string[] args)
{
multitasker().Wait();
}
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.
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();
}
}