async await and Task in C# - c#

I'm testing the async await and task functionality but it seems i'm missing something
When I write like this A being starting point.
void A()
{
Debug.WriteLine("pre B");
B();
Debug.WriteLine("post B");
}
async void B()
{
Debug.WriteLine("pre C");
await C();
Debug.WriteLine("post C")
}
async Task C()
{
await Task.Yield();
Debug.WriteLine("pre D");
await D();
Debug.WriteLine("post D");
}
async Task D()
{
Debug.WriteLine("inside D");
}
I get output in debug console as
pre B,
pre C,
pre D,
inside D,
post D,
post C,
post B
Is it running asynchronously?? I guess not, what thought it should run like
pre B,
pre C,
post B,
post C,
pre D,
inside D,
post D
So what's the problem here?

I recommend you read my async intro and follow up with my async best practices article. In particular, one of the best practices is to only use async void for event handlers.
The whole point of async is to enable asynchronous code while keeping that code written in a similar way to synchronous code. So if an outer method awaits the task returned from an inner method, then the outer method will not continue executing until the inner method completes.
So, for example, synchronous code may look like this:
void A()
{
Debug.WriteLine("pre B");
B();
Debug.WriteLine("post B");
}
void B()
{
Debug.WriteLine("inside B");
Thread.Sleep(1000);
Debug.WriteLine("still inside B");
}
The corresponding asynchronous code would be:
async Task A()
{
Debug.WriteLine("pre B");
await B();
Debug.WriteLine("post B");
}
async Task B()
{
Debug.WriteLine("inside B");
await Task.Delay(1000);
Debug.WriteLine("still inside B");
}
Even though the code will now execute asynchronously, the output is exactly the same as the synchronous version.
If you want one method to start concurrent operations and then continue, you can just call the methods and then do the await later:
async Task A()
{
// Start both tasks
Debug.WriteLine("pre B1");
Task b1 = B();
Debug.WriteLine("post B1, pre B2");
Task b2 = B();
Debug.WriteLine("post B2, pre await");
// (asynchronously) wait for them to complete
await Task.WhenAll(b1, b2);
Debug.WriteLine("post await");
}
async Task B()
{
Debug.WriteLine("inside B");
await Task.Delay(1000);
Debug.WriteLine("still inside B");
}
And in this case, you'll see both executions of B start, and A will continue executing after both Bs complete.

I changed the code now it looks like
using System;
using System.Threading.Tasks;
namespace TaskClass
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Pre A");
A();
Console.WriteLine("Post A");
Console.ReadKey();
}
static async Task A()
{
Console.WriteLine("pre B");
await B();
Console.WriteLine("post B");
}
static async Task B()
{
Console.WriteLine("inside B");
await Task.Delay(10000);
Console.WriteLine("still inside B");
}
}
}
And output i'm getting is as suspected.
Pre A
pre B
inside B
Post A
still inside B
post B
So you see as soon as I'm hitting await Task.Delay(10000); the control goes back to Main method, as evident from the fact that its printing Post A before still inside B also after Console.WriteLine("Post A"); It waits for Console.ReadKey(); only because as soon as I hit any key execution stops. It doesn't wait for Console.WriteLine("still inside B");.
So simple yet so strange. .net!! :)

I guess, that it runs sync cuz you`re using await statements, which waits for your methods to be done.
So if you want to get result like 2d variant - try not to use await

That's normal. Async/Await means that the method is suspended until the task is complete. That task runs asynchronously but the current thread waits for it and the current thread is no blocked.
This is specially useful if you don't want to block the ui thread when a method waits for a result that may be long to come.
From MSDN
Async Improves Responsiveness
Asynchrony is essential for activities that are potentially blocking,
such as when your application accesses the web. Access to a web
resource sometimes is slow or delayed. If such an activity is blocked
within a synchronous process, the entire application must wait. In an
asynchronous process, the application can continue with other work
that doesn't depend on the web resource until the potentially blocking
task finishes.
The following table shows typical areas where asynchronous programming
improves responsiveness. The listed APIs from the .NET Framework 4.5
and the Windows Runtime contain methods that support async
programming.
Application area
Supporting APIs that contain async methods
Web access
HttpClient , SyndicationClient
Working with files
StorageFile, StreamWriter, StreamReader, XmlReader
Working with images
MediaCapture, BitmapEncoder, BitmapDecoder
WCF programming
Synchronous and Asynchronous Operations
Asynchrony proves especially valuable for applications that access the
UI thread because all UI-related activity usually shares one thread.
If any process is blocked in a synchronous application, all are
blocked. Your application stops responding, and you might conclude
that it has failed when instead it's just waiting
For example, you can have a method that returns a value.
public async Task<int> LongComputation()
And then, in your code you have:
var result = await LongComputation();
The current thread is not blocked. When the result is available, you get it in your variable and you can continue with it in your current thread.
If you want to launch a new task asynchronously, you can do it like that
Task.Factory.StartNew(async () =>
{
Debug.WriteLine("async start");
await Task.Delay(5000);
Debug.WriteLine("async end");
});
In your method B, you can try the following
async void B()
{
Debug.WriteLine("pre C");
var taskC = C();
Debug.WriteLine("post C")
await taskC;
}

Related

C# - async/await print order

I am trying to understand how async and await works so i wrote this little program to try and print "b" before "a" but it's not working and i can't figure it out.. any help? :)
public class program
{
static async Task Main()
{
await a();
b();
}
async static Task a()
{
Console.WriteLine("Waiting for a");
for (int i = 0; i < 100000000; i++)
if (i % 1000000 == 0)
Console.Write(".");
Console.WriteLine("\na");
}
static void b()
{
Console.WriteLine("b");
}
}
Lets refine your code. First it doesn't work, because you told the code to await a(), and so nothing after that line can run before (or during) it. Lets fix it:
static async Task Main()
{
var task = a();
b();
await task;
}
However that is not enough, it still doesn't work. It still runs a() before b() because your a method is not asynchronous. In fact it is synchronous. Just because you use async/await it doesn't make the code automatically asynchronous. You'll need more than that. One way is to add await Task.Yield() which is truely asynchronous call, even though it does nothing (except for allowing the switch to happen). Like this:
async static Task a()
{
Console.WriteLine("Waiting for a");
for (int i = 0; i < 100000000; i++)
if (i % 1000000 == 0)
{
Console.Write(".");
await Task.Yield();
}
Console.WriteLine("\na");
}
Now you will see b() running in the middle of a(). You can play around with the await Task.Yield(); call, and put it in different places to see different result.
This is still a bit primitive example. It would be better to make b() asynchronous as well. And use delays. Like this:
public class program
{
static async Task Main()
{
var task1 = a();
var task2 = b();
await task1;
await task2;
}
async static Task a()
{
Console.WriteLine("Waiting for a");
for (var i = 0; i < 100; i++)
{
Console.Write(".");
await Task.Delay(20);
}
Console.WriteLine("\na");
}
static async Task b()
{
await Task.Delay(200);
Console.WriteLine("b");
}
}
Note that I've slightly modified your a() so that it is more time dependent, less cpu-speed dependent.
Of course you have a thread-safety issue now, which in this case is ok since Console and Task methods are thread-safe. But generally you have to be aware that when two or more Tasks run concurrently then they may run on different threads, and thus you have to make the code thread-safe.
There are 2 problems.
Await is being used incorrectly
You use await to ensure whichever async method is called is completed before any subsequent code is executed.
As freakish said in his answer, you can assign the task from method a to a variable and await it after method b.
Method a isn't actually asynchronous
There isn't any thing that can actually be awaited in the method so it would just run synchronously and complete before method b. Adding something that can be awaited like Task.Yeild (as freakish has stated) would resolve the issue.
I would recommend reading more about asynchronous programming, here is a good place to start: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/

Understanding of C# await operator

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.

Why await an async function directly not work without assigning it to a Task variable

Typically, I do the following
public static async Task dosth()
{
List<Task> job = new List<Task>();
for (int i = 0; i < 3; i++)
{
job.Add(sleep());
}
Task.WhenAll(job.ToArray());
}
static async Task sleep()
{
await Task.Delay(1000);
Console.WriteLine("Finish new");
}
It works smoothly, no problem. But when I do a review on my own code (trying using other syntax to do the same job), I suddenly figure out the following two are different.
public static async Task dosthA()
{
//This will be working synchronously, take 3 seconds.
await sleep();
await sleep();
await sleep();
//This will be working asynchronously, take 1 second only.
Task A = sleep();
Task B = sleep();
Task C = sleep();
await A;
await B;
await C;
}
Why assigning the async function to a new variable make difference? I originally think they are the same.
Update
Why it is confusing me is, actually in Microsoft doc on Async-await,
They stated the following in their code.
// Calls to TaskOfTResult_MethodAsync
Task<int> returnedTaskTResult = TaskOfTResult_MethodAsync();
int intResult = await returnedTaskTResult;
// or, in a single statement
int intResult = await TaskOfTResult_MethodAsync();
They are actually different, why they use //or , in a single statement, just because it makes no different in their own example?
This is because when you are returning a running Task when you call Sleep() even when you're assigning to a variable.
The confusion is that the Task does not begin if you assign it to a variable (A, B, or C) until you call await A; but that's not true. As soon as you assign sleep(); to A, sleep() was called; therefore the Task in the sleep() method is running. Assigning it to a variable or not the Task begins when you call the method; because in the method you start the Task.
Knowing this; when you call:
await A;
await B;
await C;
A, B, and C, have already starting simultaneously... After awaiting A it is most likely B, and C have also completed or are milliseconds from completing.
There are situations where you can reference a Task that hasn't started yet but you would have to purposely return a non-running Task to do that.
To answer the edit to your question also.
Tasks have a method called GetAwaiter() which returns a TaskAwaiter. In C# when you write var task = sleep(); then you're assigning the actual Task to the task variable. All the same when you write await sleep(); the compiler does some cool stuff and it actually calls the Task.GetAwaiter() method; which is subscribed to. The Task will run and when it is complete the TaskAwaiter fires the continuation action. This can't be explained in a simple answer but to know the outer logic helps.
Among other things the TaskAwaiter implements ICriticalNotifyCompletion which in turn implements INotifyCompletion. Both have one method each, OnCompleted(Action) and UnsafeOnCompleted(Action) (you can guess which is which by naming convention).
Another thing to note is that Task.GetAwaiter() returns a TaskAwaiter but Task<TResult>.GetAwaiter() returns a TaskAwaiter<TResult>. There's not a strong difference in the two but there is a difference in the GetResult() method of the two tasks; which is what's called while marshalling back to the proper threading context. The TaskAwaiter.GetResult() returns void and the TaskAwaiter<TResult>.GetResult() returns TResult.
I feel like if I push further into this I'll have to write pages to explain it all in detail... Hopefully just explaining your question and pulling the curtain back a little bit will shed enough light to help you both understand and dig deeper if you're more curious.
Ok, so based on the comment below I want to describe my answer a little bit further.
I'll start this simple; let's just make a Task; one that isn't running, and look at it first.
public Task GetTask()
{
var task = new Task(() => { /*some work to be done*/ });
//Now we have a reference to a non-running task.
return task;
}
We can now call code like:
public async void DoWork()
{
await GetTask();
}
… but we'll be waiting forever; until the application ends, because the Task was never started. However; we could do something like this:
public async void DoWork()
{
var task = GetTask();
task.Start();
await task;
}
… and it will await the running Task and continue once the Task is complete.
Knowing this you can make as many calls to GetTask() as you like and you'll only be referencing Tasks that have not started.
In your code it's just the opposite, which is fine, as this is the most used way. I encourage you to make sure your method names notify the user of how you're returning the Task. If the Task is already running the most common convention is the end the method name with Async. Here's another example doing it with a running Task for clarity.
public Task DoTaskAsync()
{
var task = Task.Run(() => { /*some work to be done*/ });
//Now we have a reference to a task that's already running.
return task;
}
And now we will most likely call this method like:
public async void DoWork()
{
await DoTaskAsync();
}
However; note that if we simply want to reference the Task just like we did earlier, we can, the only difference is this Task is running where the one prior was not. So this code is valid.
public async void DoWork()
{
var task = DoTaskAsync();
await task;
}
The big take away is how C# handles the async / await keywords. async tells the compiler that the method is going to become a continuation of a Task. In short; the compiler knows to look for all await calls and put the rest of the method in a continuation.
The await keyword tells the compiler to call the Task.GetAwaiter() method on the Task ( and basically subscribe to the INotifyCompletion and ICriticalNotifyCompletion) to signal the continuation in the method.
And this I wanted to add just incase you weren't aware. If you do have more than one task that you want to await but would rather await one task as if they were all one then you can do that with Task.WhenAll() So instead of:
var taskA = DoTaskAsync();
var taskB = DoTaskAsync();
var taskC = DoTaskAsync();
await taskA;
await taskB;
await taskC;
You could write it a little cleaner like so:
var taskA = DoTaskAsync();
var taskB = DoTaskAsync();
var taskC = DoTaskAsync();
await Task.WhenAll(taskA, taskB, taskC);
And there are more ways of doing this sort of thing built in; just explore it.

c# async methods in asp.net mvc actions

Let's have the following situation
// method from class A
public async Task FooAsync()
{
return Task.Factory.StartNew(() => { //code_block_1 });
}
// method from class B
public async Task BarAsync()
{
return Task.Factory.StartNew(() => { //code_block_2 });
}
and an action method in a MVC controller
public async void SomeAction()
{
A a = new A();
B b = new B();
Task t1 = a.FooAsync();
Task t2 = b.BarAsync();
// Some other code 1
await Task.WhenAll(t1, t2);
// Some other code 2
}
And a request to that action is sent. How I think everything should work:
A thread from the thread pool will be assigned to process the request
The execution of SomeAction() starts in that thread. "a" and "b" are created.
FooAsync() is called. Task.Factory.StartNew() is executed which creates new task for "//code_block_1". From that moment it's up to the TaskScheduler what thread from the thread pool will process the task. It could be the same thread executing "SomeAction" (in that case the execution of FooAsync would be synchronous) or another thread from the pool.
BarAsync() is called. [Same as above]
"// Some other code 1" is executed on the same thread on which SomeAction() started.
await Task.WhenAll(t1, t2); creates a task which waits until both t1 and t2 have completed. Everything after the "await" ("// Some other code 2") is marked as "continuation" and the current thread is release back to the pool. Once both t1 and t2 have completed, the rest of the code can be processed by a thread from the pool (not necessarily the same thread which started processing SomeAction()).
And finally my question... Am I right? Am I wrong about something and what is it? And am I missing something?
Yes, this is almost entirely accurate.
It could be the same thread executing "SomeAction"
If that was the case there would be no point in calling StartNew. This method is meant to create parallelism. It never inlines the task. You probably confused this with Wait inlining unstarted tasks.
Note, that because of the parallelism you initiated request processing will block 2 threads instead of one. Hadn't you used await it would have taken 3.

How do I create a Task that uses await inside the body that behaves the same as the synchronous version when Wait is called?

I have some code that creates a task that does some slow work like this:
public static Task wait1()
{
return new Task(() =>
{
Console.WriteLine("Waiting...");
Thread.Sleep(10000);
Console.WriteLine("Done!");
});
}
In the real implementation, the Thread.Sleep will actually be a web service call. I would like to change the body of the method can use await (so it does not consume a thread during the network access/sleep). My first attempt (based on shotgun-debugging the compile errors) was this:
public static Task wait2()
{
return new Task(async () =>
{
Console.WriteLine("Waiting...");
await Task.Delay(10000);
Console.WriteLine("Done!");
});
}
However; this task doesn't seem to behave the same as the first one, because when I call .Wait() on it; it returns immediately.
Below is a full sample (console app) showing the differences (the app will end immediately when the second task starts).
What do I need to do so that I can call Start and Wait on a Task which happens to have code using await inside it? The tasks are queued and executed later by an agent, so it's vital that the task is not auto-started.
class Program
{
static void Main(string[] args)
{
var w1 = wait1();
w1.Start();
w1.Wait(); // This waits 110 seconds
var w2 = wait2();
w2.Start();
w2.Wait(); // This returns immediately
}
public static Task wait1()
{
return new Task(() =>
{
Console.WriteLine("Waiting...");
Thread.Sleep(10000);
Console.WriteLine("Done!");
});
}
public static Task wait2()
{
return new Task(async () =>
{
Console.WriteLine("Waiting...");
await Task.Delay(10000);
Console.WriteLine("Done!");
});
}
}
It seems like this isn't possible! See alexm's answer here:
Tasks returned by async methods are always hot i.e. they are created in Running state.
:-(
I've worked around this by making my agent queue Func<Task>s instead, and the overload that receives a task simply queues () => task. Then; when de-queing a task, I check if it's not running, and if so, start it:
var currentTask = currentTaskFunction();
if (currentTask.Status == TaskStatus.Created)
currentTask.Start();
It seems a little clunky to have to do this (if this simple workaround works; why the original restriction on async methods always being created hot?), but it seems to work for me :-)
You could write this as:
public static async Task Wait2()
{
Console.WriteLine("Waiting...");
await Task.Delay(10000);
Console.WriteLine("Done!");
}
In general, it's rarely a good idea to ever use new Task or new Task<T>. If you must launch a task using the ThreadPool instead of using the async/await language support to compose one, you should use Task.Run to start the task. This will schedule the task to run (which is important, tasks should always be "hot" by conventions).
Note that doing this will make it so you don't have to call Task.Start, as well.
To help you understand this realize that async / await essentially does not create a new thread but rather it schedules that portion of code to be ran at an available point in time.
When you create the new Task(async () => ...) you have a task that run an async method. When that inner async method hits an await the 'new Task' is considered complete because the rest of it has been scheduled. To help you understand better place some code (a lot if wanted) in the 'new Task' before the await command. It will all execute before the application terminates and once await is reached that task will believe it has completed. It then returns and exits the application.
The best way to avoid this is to not place any task or async methods inside of your task.
Remove the async keyword and the await keyword from the method and it will work as expected.
This is the same as creating a callback if you're familiar with that.
void MethodAsync(Action callback)
{
//...some code
callback?.Invoke();
}
//using this looks like this.
MethodAsync(() => { /*code to run when complete */});
//This is the same as
Task MethodAsync()
{
//... some code here
}
//using it
await MethodAsync();
/*code to run when complete */
The thing to understand is that you're creating a new task within a task basically. So the inner 'callback' is being created at the await keyword.
You're code looks like this..
void MethodAsync(Action callback)
{
//some code to run
callback?.Invoke(); // <- this is the await keyword
//more code to run.. which happens after we run whoever is
//waiting on callback
}
There's code missing obviously. If this doesn't make sense please feel free to contact me and I'll assist. async / await (meant to make things simpler) is a beast to wrap your head around at first. Afterward you get it then it'll probably be your favorite thing in c# since linq. :P
Try this:
public async static Task wait2()
{
Console.WriteLine("Waiting...");
await Task.Delay(2000);
Console.WriteLine("Done!");
}
But we aware that the task is already started so you don't have to call start:
var w2 = wait2();
//w2.Start();
w2.Wait();
I think the problem with your wait2 function is that is creating 2 task, the one in new Task(...) and another in Task.Delay(). You are waiting for the first one, but you are not waiting for the inner one.

Categories

Resources