I just have some basic understanding of C#'s asynchronous programming. As I understand, this code
statement1;
await statement2();
statement3;
statement4;
should logically be equivalent to
statement1;
var awaiter = statement2().GetAwaiter();
awaiter.OnCompletion(() => {
awaiter.GetResult();
statement3;
statement4;
});
So if there is an await statement in the code, all the following statements will only start to execute after the awaited task completed. This seems just like synchronous code. The document states that the await statement will cause the execution return to the caller and resume after the awaited task completes. But I can't fully understand how it works in the following example.
using System;
using System.Threading.Tasks;
namespace ThreadTesting
{
class Program
{
public static async Task Main(string[] args)
{
await DoSomething2(); // <------- B
Console.WriteLine("Test!"); // <------- C
Console.ReadKey(); // <------- D
}
public static async Task DoSomething2()
{
Console.WriteLine("Start DoSomething2");
var i = await DoSomething(); // <------ A
Console.WriteLine("End DoSomething2");
}
public static async Task<int> DoSomething()
{
Console.WriteLine("Start DoSomething1");
await Task.Delay(10000);
Console.WriteLine("Before returning from DoSomething1");
return 88;
}
}
}
The output is
Start DoSomething2
Start DoSomething1
Before returning from DoSomething1
End DoSomething2
Test!
Let's take statement A for example. Here it's awaiting DoSomething(). While awaiting it, the execution logic returns from DoSomething2() and goes back to B. Here as I understand, the execution should go on to execute statement C and D while awaiting DoSomething2() (statement B). But the result shows that the string "Test!" only be printed at the end of the program. Why this happens? Is my understanding correct?
The catch is that your Main returns Task, so it is also fully async and will be "stopped". And your execution order will look like this:
Main() starts
Main gets to the await operator and creates a Task with the rest of the method. It looks as if the Main() has stopped, waiting for the await result
DoSomething2() starts and writes to the console "Start DoSomething2"
It gets to the next await operator and creates another Task with the rest of the DoSomething() method
DoSomething() starts
It writes to the console "Start DoSomething1"
Gets to the awat operator and creates a Task with the rest of the DoSomething() method, waiting for a delay.
After this Task is completed (after a delay) DoSomething() writes "Before returning from DoSomething1" to console and returns 88.
The Task, created in DoSomething2() completes the rest of the method (assigns 88 to i and writes "Before returning from DoSomething1" to console
The Task, created in Main() completes, writing "Test!" to console.
So the "Test!" is in the end because your Main() returns Task. If you want the behaviour, you expect just swap Task to void:
public static async void Main(string[] args)
{
...
}
Then when reaching the await operator, the Main() wont wait for the Task() completion and will continue the execution.
Something worked like this..
You have to go through all function then back to where call await for it.
using System;
using System.Threading.Tasks;
namespace ThreadTesting
{
class Program
{
public static async Task Main(string[] args)
{
await DoSomething2();// <------ Start go to DoSomething2
Console.WriteLine("Test!"); //F
Console.ReadKey();
}
public static async Task DoSomething2()
{
Console.WriteLine("Start DoSomething2"); // <------ A godown
var i = await DoSomething(); // <------ B go to DoSomething
Console.WriteLine("End DoSomething2");// <------ E back to Main
}
public static async Task<int> DoSomething()
{
Console.WriteLine("Start DoSomething1");// <------ C godown
await Task.Delay(10000);
Console.WriteLine("Before returning from DoSomething1");// <------ D back to DoSomething2
return 88;
}
}
}
await and async do stop the execution of the code for you. But they actually do not stop the execution of the main thread.
In your example, everything looks like working as expected.
Here as I understand, the execution should go on to execute statement C and D while awaiting DoSomething2()
No, they shouldn't because you said you want to wait till it is done. Even if it does not return something, it will wait because of await. Inside DoSomething2() you do the same thing.
await is more reasonable when you depend on the result of an asynchronous function. It saves you from callback hell.
Instead of this
you can use this
note: screenshots are from https://blog.hellojs.org/asynchronous-javascript-from-callback-hell-to-async-and-await-9b9ceb63c8e8 The codes are in JS.
As canbax already told in his answer the await operator does excatly what its naming suggests. It Asynchronously waits that the Task (or better any type that can be awaited) completes.
So if you have code like
DoSomething1();
await DoSomething2Async();
DoSomething3();
the call to DoSomething3 will only ocure after the task returned by the call to DoSomething2Async has finished with out errors, regardless of what happens in DoSomething2Async
In contrast you can creat a task by calling an async method and only later await the task, like:
DoSomething1();
Task task = DoSomething2Async();
DoSomething3();
await task;
this code is more open to the order of calling DoSomething3 and the completion of the task. This is because you basicly tell the compiler "start doing DoSomething2Async, but I only care that it finishes after the call to DoSomething3 has finished.
Here is a live example for
using System;
using System.Threading.Tasks;
class Program {
static async Task Main(string[] args) {
DoSomething1();
await DoSomething2Async();
DoSomething3();
Console.WriteLine("---");
DoSomething1();
var task = DoSomething2Async();
DoSomething3();
await task;
}
static void DoSomething1()
{
Console.WriteLine("1");
}
static async Task DoSomething2Async()
{
await Task.Run(() => Console.WriteLine("2"));
}
static void DoSomething3()
{
Console.WriteLine("3");
}
}
which will mostlikly produce output like
1
2
3
---
1
3
2
I found the issue with my code. Just as I described in the question, this code
statement1;
await statement2();
statement3;
statement4;
roughly equivalent to
statement1;
var awaiter = statement2().GetAwaiter();
awaiter.OnCompletion(() => {
awaiter.GetResult();
statement3;
statement4;
});
This is true. So for the code
await DoSomething2(); // <------- B
Console.WriteLine("Test!"); // <------- C
Console.ReadKey(); // <------- D
it roughly equivalent to
statement1;
var awaiter = DoSomething2().GetAwaiter(); // <------- B
awaiter.OnCompletion(() => {
awaiter.GetResult();
Console.WriteLine("Test!"); // <------- C
Console.ReadKey(); // <------- D
});
That is to say, the statement C & D will only execute after the task DoSomething2() completed. That's why Test! will only be printed at the end of the program. If I remove the await before DoSomething2():
using System;
using System.Threading.Tasks;
namespace ThreadTesting
{
class Program
{
public static void Main(string[] args)
{
DoSomething2(); // <------- B (await removed)
Console.WriteLine("Test!"); // <------- C
Console.ReadKey(); // <------- D
}
public static async Task DoSomething2()
{
Console.WriteLine("Start DoSomething2");
var i = await DoSomething(); // <------ A
Console.WriteLine("End DoSomething2"); // <------ I
}
public static async Task<int> DoSomething()
{
Console.WriteLine("Start DoSomething1"); // <------ E
await Task.Delay(10000); // <------ F
Console.WriteLine("Before returning from DoSomething1"); // <------ G
return 88; // <------ H
}
}
}
The program will return this result:
Start DoSomething2
Start DoSomething1
Test!
Before returning from DoSomething1
End DoSomething2
This is reasonable and is exactly what I want. The execution logic can be logically understood as:
Start at main and run DoSomething2()
Go to DoSomething2 and print "Start DoSomething2"
Execute DoSomething() (statement A) and print "Start DoSomething1" (statement E)
Execute Task.Delay(10000) (statement F). As it takes some time and returns a task (an awaitable), add the following statements (statement G & H) as a continuation (C1) and return to statement A.
As statement A is an await statement, it adds the following statement (statement I) as a continuation (c2) and returns to statement B.
The execution goes to B and the thread is not blocked. The code continue to execute statement C and D.
Once statement F completed, the continuation C1 starts to execute.
Once C1 completed, the continuation C2 starts to execute.
This is my understanding of the logic. Please correct me if you find any error. I'm new to asynchronous programming.
The misconception I have is that I though I have to await all async methods. This is not true.
Related
In programs utilizing async-await, my understanding is like this:
an async method that IS NOT awaited will run in the background (?) and rest of the code will continue to execute before this non-awaited method finishes
an async method that IS awaited will wait until the method finishes before moving on to the next lines of code
The application below was written by me to check if the above statements are correct.
using System;
using System.Threading.Tasks;
namespace ConsoleApp3
{
class Program
{
static async Task Main(string[] args)
{
Console.WriteLine("Hello World!");
DoJob();
var z = 3;
Console.ReadLine();
}
static async Task DoJob()
{
var work1 = new WorkClass();
var work2 = new WorkClass();
while (true)
{
await work1.DoWork(500);
await work2.DoWork(1500);
}
}
}
public class WorkClass
{
public async Task DoWork(int delayMs)
{
var x = 1;
await Task.Delay(delayMs);
var y = 2;
}
}
}
Here are some of my observations:
The DoJob(); call is not awaited. However, the debugger shows me that the code inside of DoJob is being executed, just as if it was a normal non-async method.
When code execution gets to await work1.DoWork(500);, I would think "OK, so maybe now the DoJob method will be left and var z = 3; will be executed? After all, 'await' should leave the method." In reality, it just goes into DoWork and doesn't leave DoJob - var z = 3; is still not executed.
Finally, when execution reaches await Task.Delay(delayMs);, DoJob is left, and the var z = 3; is reached. After that, code after the Delay is executed.
The things that I don't understand:
Why does await Task.Delay(delayMs); leave the DoJob method, but await work1.DoWork(500); does not?
I see that DoJob is executing normally. I thought it would be done in the background (maybe by one of the thread pool threads?). Looks like it could block the thread if it was some long-running method, am I right?
Why does await Task.Delay(delayMs); leave the DoJob method, but await work1.DoWork(500); does not?
Because this code:
await work1.DoWork(500);
is the same as this code:
var task = work1.DoWork(500);
await task;
So your code is calling the method first, and then awaiting the returned task. It's common to talk about await as "awaiting method calls", but that's not what actually happens - technically, the method call is done first (synchronously), and then the returned task is awaited.
I see that DoJob is executing normally. I thought it would be done in the background (maybe by one of the thread pool threads?).
No; with true asynchronous operations, there is no thread that is blocked on that operation.
Looks like it could block the thread if it was some long-running method, am I right?
Yes.
my understanding is like this
I recommend reading my async intro for a better mental framework. In summary:
async enables the await keyword. It also generates a state machine that handles creating the Task return value and stuff like that.
await operates on an "awaitable" (usually a task). First, it checks to see if it's already complete; if it is, the async method continues executing synchronously.
If the awaitable is not already complete, then await (by default) captures its context and schedules the continuation of the async method to run on that context when the awaitable completes.
The compiler splits the code in an async method in chunks. 1 before the first await and 1 between each await and 1 after the last await.
The execution will return to the caller at the first non completed awaiter or the end of the method.
This method will only return a completed Task after fully executed:
async Task M1() => await Task.CompletedTask;
This method will only return an incomplete Task that will complete when the Task returned by Task.Dealy(1000) is completed:
async Task M2() => await Task.Delay(1000);
Here's a small example:
static async Task Main(string[] args)
{
var t = TwoAwaits();
Console.WriteLine("Execution returned to main");
await t;
}
private static async Task TwoAwaits()
{
Console.WriteLine("Before awaits");
await Task.CompletedTask;
Console.WriteLine("Between awaits #1");
await Task.Delay(1000);
Console.WriteLine("Between awaits #2");
await Task.Delay(1000);
Console.WriteLine("After awaits");
}
/*
Before awaits
Between awaits #1
Execution returned to main
Between awaits #2
After awaits
*/
Let's look at the four possibilities:
(1)
void Main()
{
Console.WriteLine($"Main 0 - {Thread.CurrentThread.ManagedThreadId}");
DoJob();
Console.WriteLine($"Main 1 - {Thread.CurrentThread.ManagedThreadId}");
}
public static async Task DoJob()
{
Console.WriteLine($"DoJob 0 - {Thread.CurrentThread.ManagedThreadId}");
Thread.Sleep(2000);
Console.WriteLine($"DoJob 1 - {Thread.CurrentThread.ManagedThreadId}");
}
This outputs:
Main 0 - 14
DoJob 0 - 14
DoJob 1 - 14
Main 1 - 14
It has a 2 second pause after DoJob 0.
(2)
async Task Main()
{
Console.WriteLine($"Main 0 - {Thread.CurrentThread.ManagedThreadId}");
await DoJob();
Console.WriteLine($"Main 1 - {Thread.CurrentThread.ManagedThreadId}");
}
public static async Task DoJob()
{
Console.WriteLine($"DoJob 0 - {Thread.CurrentThread.ManagedThreadId}");
Thread.Sleep(2000);
Console.WriteLine($"DoJob 1 - {Thread.CurrentThread.ManagedThreadId}");
}
Again this outputs:
Main 0 - 14
DoJob 0 - 14
DoJob 1 - 14
Main 1 - 14
(3)
async Task Main()
{
Console.WriteLine($"Main 0 - {Thread.CurrentThread.ManagedThreadId}");
await DoJob();
Console.WriteLine($"Main 1 - {Thread.CurrentThread.ManagedThreadId}");
}
public static Task DoJob()
{
return Task.Run(() =>
{
Console.WriteLine($"DoJob 0 - {Thread.CurrentThread.ManagedThreadId}");
Thread.Sleep(2000);
Console.WriteLine($"DoJob 1 - {Thread.CurrentThread.ManagedThreadId}");
});
}
This has different output because it has changed thread:
Main 0 - 15
DoJob 0 - 13
DoJob 1 - 13
Main 1 - 13
And finally:
async Task Main()
{
Console.WriteLine($"Main 0 - {Thread.CurrentThread.ManagedThreadId}");
DoJob();
Console.WriteLine($"Main 1 - {Thread.CurrentThread.ManagedThreadId}");
}
public static Task DoJob()
{
return Task.Run(() =>
{
Console.WriteLine($"DoJob 0 - {Thread.CurrentThread.ManagedThreadId}");
Thread.Sleep(2000);
Console.WriteLine($"DoJob 1 - {Thread.CurrentThread.ManagedThreadId}");
});
}
This has a different output again:
Main 0 - 13
Main 1 - 13
DoJob 0 - 12
DoJob 1 - 12
In this last case it is not waiting for DoJob because DoJob is running on a different thread.
So if you follow the logic here the issue is that async/await doesn't create (or use) a different thread. The method called must do that.
Before Async & Await, there was two type of methods. Those who returned the result directly, and those who received a callback function as a parameter. On the latter, the method was invoked in the same thread syncronously and returned no value, and later, on the same or different thread your callback function would have been called with the result. Historically all I/O (disk, network, even memory) worked with callbacks (actually: interrupts) but medium to high level languages like C# would mask all that internally so end users don't need to learn/write low level code.
This worked pretty well up to a point, except this optimization wasted some physical resources. For example Node.js outperformed several other languages/server platforms by their limitation that forces the developers to use the callback model instead of the 'managed' mode.
This pushed C# and other languages to go back to the callback model, but the code readability really suffered (code callback spaguetti). So Async and Await was introduced.
Async and await let's you write in the 'callback model' with 'managed' syntax. All callbacks are handled by the compiler.
Each time you write 'await' in an async method, your method is actually split into two methods connected by a callback.
Now, you can write an async method that does regular sync code, without awaits, nor thread switch or I/O. That 'async' method will actually run synchronously. So, it is actually the same to await method1() or call without await. Why? because your async call is not awaiting anything, so your async code is still one piece of continous code.
If inside your method you await one, two or more different methods, then your method will be split into one, two or more pieces. And only the first piece will be guaranteed to be run synchronously. All the other pieces will run on other thread depending on the code that you are awaiting.
TL;DR;
Async/Await method is does not guarantees multi-threading or parallel processing. That will actually depend on the payload (the called async method). For example, http downloads will tipically be paralellized if you manage your awaits because those are functions that are mostly waiters of an external response. On the other side, heavy CPU processing, like compressing a file, will require other form of cpu/thread management not provided by async/await.
If you do not await an async method, your code will surely run synchronously up to the first await of the called method, given it has one. But later on, it may or not run sync.
why does await Task.Delay(delayMs); leave the DoJob method, but await work1.DoWork(500); does not?
Because, up and until there is an actual asynchronous call, it's still in the same context. If DoWork was just:
public async Task DoWork(int delayMs)
{
var x = 1;
var y = 2;
return Task.CompletedTask;
}
there would be no need for a continuation and hence, you would debug all the way without "jumping" back to the orignal await call.
Here is how your application could be remodeled if you were forced to avoid async/await for some reason. See how complex it gets to replicate the logic inside the loop. Async/await is really a gift from the heavens!
using System;
using System.Threading.Tasks;
namespace ConsoleApp3
{
class Program
{
static Task Main(string[] args)
{
Console.WriteLine("Hello World!");
DoJob();
var z = 3;
Console.ReadLine();
return Task.CompletedTask;
}
static Task DoJob()
{
var work1 = new WorkClass();
var work2 = new WorkClass();
var tcs = new TaskCompletionSource<bool>();
Loop();
return tcs.Task;
void Loop()
{
work1.DoWork(500).ContinueWith(t1 =>
{
if (t1.IsFaulted) { tcs.SetException(t1.Exception); return; }
work2.DoWork(1500).ContinueWith(t2 =>
{
if (t2.IsFaulted) { tcs.SetException(t2.Exception); return; }
if (true) { Loop(); } else { tcs.SetResult(true); }
// The 'if (true)' corresponds to the 'while (true)'
// of the original code.
});
});
}
}
}
public class WorkClass
{
public Task DoWork(int delayMs)
{
var x = 1;
int y;
return Task.Delay(delayMs).ContinueWith(t =>
{
if (t.IsFaulted) throw t.Exception;
y = 2;
});
}
}
}
Let's assume I have these methods:
public async Task<Something> GetSomethingAsync()
{
var somethingService = new SomethingService();
return await service.GetAsync();
}
and
public Task<Something> GetSomethingAsync()
{
var somethingService = new SomethingService();
return service.GetAsync();
}
Both options compile and work the same way. Is there any best practise as to which option is better of if one is faster then the other?
Or is it just some syntactic sugar?
In the first method compiler will generate "state machine" code around it and execution will be returned to the line return await service.GetAsync(); after task will be completed. Consider example below:
public async Task<Something> GetSomethingAsync()
{
var somethingService = new SomethingService();
// Here execution returns to the caller and returned back only when Task is completed.
Something value = await service.GetAsync();
DoSomething();
return value;
}
The line DoSomething(); will be executed only after service.GetAsync task is completed.
Second approach simply starts execution of service.GetAsync and return correspondent Task to the caller without waiting for completion.
public Task<Something> GetSomethingAsync()
{
var somethingService = new SomethingService();
Task<Something> valueTask = service.GetAsync();
DoSomething();
return valueTask;
}
So in the example above DoSomething() will be executed straight after line Task<Something> valueTask = service.GetAsync(); without waiting for completion of task.
Executing async method on the another thread depend on the method itself.
If method execute IO operation, then another thread will be only waste of the thread, which do nothing, only waiting for response. On my opinion async - await are perfect approach for IO operations.
If method GetAsync contains for example Task.Run then execution goes to the another thread fetched from thread pool.
Below is short example, not a good one, but it show the logic a tried to explain:
static async Task GetAsync()
{
for(int i = 0; i < 10; i++)
{
Console.WriteLine($"Iterate GetAsync: {i}");
await Task.Delay(500);
}
}
static Task GetSomethingAsync() => GetAsync();
static void Main(string[] args)
{
Task gettingSomethingTask = GetSomethingAsync();
Console.WriteLine("GetAsync Task returned");
Console.WriteLine("Start sleeping");
Thread.Sleep(3000);
Console.WriteLine("End sleeping");
Console.WriteLine("Before Task awaiting");
gettingSomethingTask.Wait();
Console.WriteLine("After Task awaited");
Console.ReadLine();
}
And output will be next:
Iterate GetAsync: 0
GetAsync Task returned
Start sleeping
Iterate GetAsync: 1
Iterate GetAsync: 2
Iterate GetAsync: 3
Iterate GetAsync: 4
Iterate GetAsync: 5
End sleeping
Before Task awaiting
Iterate GetAsync: 6
Iterate GetAsync: 7
Iterate GetAsync: 8
Iterate GetAsync: 9
After Task awaited
As you can see executing of GetAsync starts straight after calling it.
If GetSomethingAsync() will be changed to the:
static Task GetSomethingAsync() => new Task(async () => await GetAsync());
Where GetAsync wrapped inside another Task, then GetAsync() will not be executed at all and output will be:
GetAsync Task returned
Start sleeping
End sleeping
Before Task awaiting
After Task awaited
Of course you will need to remove line gettingSomethingTask.Wait();, because then application just wait for task which not even started.
I'm looking at the examples on http://www.dotnetperls.com/async to better understand async/await, but the following is confusing to me:
I understand why the example below is considered asynchronous. HandleFileAsync is invoked, the Console.WriteLine call is made, then we await the completion of task before proceeding.
static async void ProcessDataAsync()
{
// Start the HandleFile method.
Task<int> task = HandleFileAsync("C:\\enable1.txt");
// Control returns here before HandleFileAsync returns.
// ... Prompt the user.
Console.WriteLine("Please wait patiently " +
"while I do something important.");
// Wait for the HandleFile task to complete.
// ... Display its results.
int x = await task;
Console.WriteLine("Count: " + x);
}
However in the following example, we await a call to Task.Run which runs an action:
static async void Example()
{
// This method runs asynchronously.
int t = await Task.Run(() => Allocate());
Console.WriteLine("Compute: " + t);
}
So, if we are awaiting the completion of Task.Run here, what exactly is happening asynchronously? I thought it becomes a blocking call once as soon as we await the subsequent task's execution to complete, which, in this case is invoked on the same line.
What am I missing?
I thought it becomes a blocking call once as soon as we await the subsequent task's execution to complete, which, in this case is invoked on the same line. What am I missing?
Your belief is false; that's what you're missing. "await" means "return now, go run something else while we are asynchronously waiting, and when the result is available, come back here."
Fetching the result of the task does what you think await does. We would not have had to invent await if all it did was synchronously fetch the result of the task! It asynchronously fetches the result of the task.
While we're at it, this comment is wrong:
// Control returns here before HandleFileAsync returns.
How could that possibly be? HandleFileAsync returned a task! How did control get to that point with a task in hand if HandleFileAsync did not return? Of course it returned.
And this comment is misleading:
// Wait for the HandleFile task to complete.
That should be asynchronously wait for the task to complete. By asynchronously waiting, remember, we mean "return now, go run more work, and when the task is complete, resume at this point with the result in hand."
I would find a better tutorial if I were you.
what exactly is happening asynchronously?
consider this:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication2
{
class Program
{
static volatile uint i;
static uint Test()
{
Thread.Sleep(1000);
i = 2; //uint.MaxValue;
while (i-- > 0) { }
return i;
}
static bool done;
static async void Example()
{
var t = await Task.Run(() => Test());
Console.WriteLine("result: " + t);
done = true;
}
static void Main(string[] args)
{
Example();
Console.WriteLine("wait: Control returns here before Example() returns ");
while (!done) { }
Console.WriteLine("done ");
}
}
}
the Example(); itself is happening asynchronously.
so if you remove while (!done) { }
the program exits before Example() finishes.
I hope this helps.
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.
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();
}
}