I know this is old and there are a lot of articles and questions around however still it is confusing to understand, I have a simple basic example can someone explain it in a simplified way, basically I need to understand the flow.
I have two examples one with await and one without await, when I execute both, the output is identical, I believe I need to have 'Task.Run()' in order to execute asynchronously.
internal class Common
{
public async Task Process()
{
Console.WriteLine("Process Started");
DownloadileAsync();
Console.WriteLine("Process Completed");
}
public async Task DownloadileAsync()
{
DownloadFile();
Console.WriteLine("DownloadAsync");
}
public async Task DownloadFile()
{
Thread.Sleep(2000);
Console.WriteLine("File Downloaded");
}
}
=============================================================================
// one with await
internal class Common
{
public async Task Process()
{
Console.WriteLine("Process Started");
await DownloadileAsync();
Console.WriteLine("Process Completed");
}
public async Task DownloadileAsync()
{
await DownloadFile();
Console.WriteLine("DownloadAsync");
}
public async Task DownloadFile()
{
Thread.Sleep(2000);
Console.WriteLine("File Downloaded");
}
}
// Output
Main Thread Started
Process Started
File Downloaded
DownloadAsync
Process Completed
Main Thread Completed
The output is same for both approaches because you block the thread usign Thread.Sleep(2000) in first approach. If you want it to be really async replace it with await Task.Delay(2000) and you will see the difference in output.
Using this:
public async Task Process()
{
Console.WriteLine("Process Started");
DownloadileAsync();
Console.WriteLine("Process Completed");
}
your just start the task and don't wait for it to be finished before executing the rest code after this task.
Imagine you have some async method that returns result:
public async Task<int> GetResultAsync()
{
// instead of Thread.Sleep(2000); to avoid blocking
await Task.Delay(2000); // to simulate something like request to another API
return 10;
}
Having this method your approach without await won't work here:
public async Task Process()
{
Console.WriteLine("Process Started");
var result = GetResultAsync();
Console.WriteLine("Process Completed");
// prints System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[System.Int32,Test.Enums.Program+<GetResultAsync>d__1]
// not 10
// because type of the result is Task<int>, not just int
Console.WriteLine($"Result is: {result}");
}
So here you don't wait the task to be finished. You just start the task and forget about it (fire-and-forget).
When you have await:
public async Task Process()
{
Console.WriteLine("Process Started");
var result = await GetResultAsync(); // await here
Console.WriteLine("Process Completed");
// prints 10
Console.WriteLine($"Result is: {result}");
}
you asynchronously wait for task to be finished before continuation.
Related
I'm trying to play a bit with C#'s async/await/continuewith.
My goal is to have to have 2 tasks which are running in parallel, though which task is executing a sequence of action in order.
To do that, I planned to have a List<Task> that represent the 2 (or more) tasks running in parallel, and to use ContinueWith on each of the Task
My problem is that the callback in continue with seems not to be executed while the await taskList has already returned.
In order to summarize, here's a sample to illustrate what I'm expecting to happen:
class Program
{
static public async Task Test()
{
System.Console.WriteLine("Enter Test");
await Task.Delay(100);
System.Console.WriteLine("Leave Test");
}
static void Main(string[] args)
{
Test().ContinueWith(
async (task) =>
{
System.Console.WriteLine("Enter callback");
await Task.Delay(1000);
System.Console.WriteLine("Leave callback");
},
TaskContinuationOptions.AttachedToParent).Wait();
Console.WriteLine("Done with test");
}
}
The expected output would be
Enter Test
Leave Test
Enter callback
Leave callback
Done with test
However, the output is
Enter Test
Leave Test
Enter callback
Done with test
Is there a way to make the Task on which ContinueWith is called wait for the provided function to complete before being considered as done? ie. .Wait will wait for both tasks to be completed, the original one, and the one which is returned by ContinueWith
When chaining multiple tasks using the ContinueWith method, your return type will be Task<T> whereas T is the return type of the delegate/method passed to ContinueWith.
As the return type of an async delegate is a Task, you will end up with a Task<Task> and end up waiting for the async delegate to return you the Task which is done after the first await.
In order to correct this behaviour, you need to use the returned Task, embedded in your Task<Task>. Use the Unwrap extension method to extract it.
When you're doing async programming, you should strive to replace ContinueWith with await, as such:
class Program
{
static public async Task Test()
{
System.Console.WriteLine("Enter Test");
await Task.Delay(100);
System.Console.WriteLine("Leave Test");
}
static async Task MainAsync()
{
await Test();
System.Console.WriteLine("Enter callback");
await Task.Delay(1000);
System.Console.WriteLine("Leave callback");
}
static void Main(string[] args)
{
MainAsync().Wait();
Console.WriteLine("Done with test");
}
}
The code using await is much cleaner and easier to maintain.
Also, you should not use parent/child tasks with async tasks (e.g., AttachedToParent). They were not designed to work together.
I'd like to add my answer to complement the answer which has already been accepted. Depending on what you're trying to do, often it's possible to avoid the added complexity of async delegates and wrapped tasks. For example, your code could be re-factored like this:
class Program
{
static async Task Test1()
{
System.Console.WriteLine("Enter Test");
await Task.Delay(100);
System.Console.WriteLine("Leave Test");
}
static async Task Test2()
{
System.Console.WriteLine("Enter callback");
await Task.Delay(1000);
System.Console.WriteLine("Leave callback");
}
static async Task Test()
{
await Test1(); // could do .ConfigureAwait(false) if continuation context doesn't matter
await Test2();
}
static void Main(string[] args)
{
Test().Wait();
Console.WriteLine("Done with test");
}
}
When I call test1() method then ManagedThreadId will be changed after
Delay(1).
When I call test2() method (instead of test1()) then ManagedThreadId
will stay same.
When async-await change thread?
In test2() method time required to finish method is even longer then in test1()
[Route("api/[controller]")]
[HttpGet]
public async Task<IActionResult> Get()
{
await MainAsync();
return new ObjectResult(null);
}
static async Task MainAsync()
{
Console.WriteLine("Main Async: " + System.Threading.Thread.CurrentThread.ManagedThreadId.ToString());
await test1();
//await test2();
// . . .more code
}
private static async Task test1()
{
Console.WriteLine("thisIsAsyncStart: " + System.Threading.Thread.CurrentThread.ManagedThreadId.ToString());
await Task.Delay(1);
Console.WriteLine("thisIsAsyncEnd: " + System.Threading.Thread.CurrentThread.ManagedThreadId.ToString());
}
private static async Task test2()
{
Console.WriteLine("thisIsAsyncStart: " + System.Threading.Thread.CurrentThread.ManagedThreadId.ToString());
System.Threading.Thread.Sleep(5000);
await Task.FromResult(0);
Console.WriteLine("thisIsAsyncEnd: " + System.Threading.Thread.CurrentThread.ManagedThreadId.ToString());
}
test1 awaits Task.Delay(1), which isn't going to be completed at the time it goes to await it, meaning the rest of test1 needs to be scheduled as a continuation.
For test2 you're awaiting Task.FromResult, which will always return an already completed Task. When you await an already completed task the method can just keep running on the current thread, without needing to schedule a continuation.
Here's some code I'm working with, that fires of three tasks. My Stop is called, and from the output, it looks as though the tasks finish after Stop returns, which isn't what I'd expect.
This is Visual Studio 2013 with .NET 4.5.1
I tried the same using AutoResetEvent which worked as expected.
Here's the output from this:
Three tasks without auto reset
Starting...
FIRST
SECOND
THIRD
Running...
FIRST
FIRST
FIRST
SECOND
FIRST
FIRST
THIRD
...
SECOND
FIRST
Stopping...
THIRD
All done!
First done
Second done
Third done
What I'd expect to see is:
First done
Second done
Third done
All done!
Here's the code (I even added locking around the Console output, but it made no difference):
public class ThreeTasksWithoutAutoResetEvent
{
private CancellationTokenSource cancellation;
private Task[] tasks;
private object obj = new object();
public ThreeTasksWithoutAutoResetEvent()
{
Console.WriteLine("Three tasks without auto reset");
cancellation = new CancellationTokenSource();
tasks = new Task[3];
Message("Starting...");
Start();
Message("Running...");
Thread.Sleep(3000);
Message("Stopping...");
Stop();
Message("All done!");
}
private void Start()
{
tasks[0] = this.First(cancellation.Token);
tasks[1] = this.Second(cancellation.Token);
tasks[2] = this.Third(cancellation.Token);
}
private async void Stop()
{
cancellation.Cancel();
await Task.WhenAll(tasks);
}
private async Task First(CancellationToken token)
{
await Task.Run(
() =>
{
while (!token.IsCancellationRequested)
{
Message("FIRST");
Thread.Sleep(100);
}
Message("First done");
}, token);
}
private async Task Second(CancellationToken token)
{
await Task.Run(
() =>
{
while (!token.IsCancellationRequested)
{
Message("SECOND");
Thread.Sleep(300);
}
Message("Second done");
}, token);
}
private async Task Third(CancellationToken token)
{
await Task.Run(
() =>
{
while (!token.IsCancellationRequested)
{
Message("THIRD");
Thread.Sleep(500);
}
Message("Third done");
}, token);
}
private void Message(string message)
{
lock (obj)
{
Console.WriteLine(message);
}
}
}
Because you're not waiting for Stop method to finish. You can't wait for an async void method. You need return a Task, so that the caller can wait/await for it to complete.
private Task Stop()
{
cancellation.Cancel();
return Task.WhenAll(tasks);
}
Then you can call Stop().Wait(); instead of Stop. That will wait for WhenAll to complete.
I'm trying to play a bit with C#'s async/await/continuewith.
My goal is to have to have 2 tasks which are running in parallel, though which task is executing a sequence of action in order.
To do that, I planned to have a List<Task> that represent the 2 (or more) tasks running in parallel, and to use ContinueWith on each of the Task
My problem is that the callback in continue with seems not to be executed while the await taskList has already returned.
In order to summarize, here's a sample to illustrate what I'm expecting to happen:
class Program
{
static public async Task Test()
{
System.Console.WriteLine("Enter Test");
await Task.Delay(100);
System.Console.WriteLine("Leave Test");
}
static void Main(string[] args)
{
Test().ContinueWith(
async (task) =>
{
System.Console.WriteLine("Enter callback");
await Task.Delay(1000);
System.Console.WriteLine("Leave callback");
},
TaskContinuationOptions.AttachedToParent).Wait();
Console.WriteLine("Done with test");
}
}
The expected output would be
Enter Test
Leave Test
Enter callback
Leave callback
Done with test
However, the output is
Enter Test
Leave Test
Enter callback
Done with test
Is there a way to make the Task on which ContinueWith is called wait for the provided function to complete before being considered as done? ie. .Wait will wait for both tasks to be completed, the original one, and the one which is returned by ContinueWith
When chaining multiple tasks using the ContinueWith method, your return type will be Task<T> whereas T is the return type of the delegate/method passed to ContinueWith.
As the return type of an async delegate is a Task, you will end up with a Task<Task> and end up waiting for the async delegate to return you the Task which is done after the first await.
In order to correct this behaviour, you need to use the returned Task, embedded in your Task<Task>. Use the Unwrap extension method to extract it.
When you're doing async programming, you should strive to replace ContinueWith with await, as such:
class Program
{
static public async Task Test()
{
System.Console.WriteLine("Enter Test");
await Task.Delay(100);
System.Console.WriteLine("Leave Test");
}
static async Task MainAsync()
{
await Test();
System.Console.WriteLine("Enter callback");
await Task.Delay(1000);
System.Console.WriteLine("Leave callback");
}
static void Main(string[] args)
{
MainAsync().Wait();
Console.WriteLine("Done with test");
}
}
The code using await is much cleaner and easier to maintain.
Also, you should not use parent/child tasks with async tasks (e.g., AttachedToParent). They were not designed to work together.
I'd like to add my answer to complement the answer which has already been accepted. Depending on what you're trying to do, often it's possible to avoid the added complexity of async delegates and wrapped tasks. For example, your code could be re-factored like this:
class Program
{
static async Task Test1()
{
System.Console.WriteLine("Enter Test");
await Task.Delay(100);
System.Console.WriteLine("Leave Test");
}
static async Task Test2()
{
System.Console.WriteLine("Enter callback");
await Task.Delay(1000);
System.Console.WriteLine("Leave callback");
}
static async Task Test()
{
await Test1(); // could do .ConfigureAwait(false) if continuation context doesn't matter
await Test2();
}
static void Main(string[] args)
{
Test().Wait();
Console.WriteLine("Done with test");
}
}
I'm starting to learn about async / await in C# 5.0, and I don't understand it at all. I don't understand how it can be used for parallelism. I've tried the following very basic program:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Task task1 = Task1();
Task task2 = Task2();
Task.WaitAll(task1, task2);
Debug.WriteLine("Finished main method");
}
public static async Task Task1()
{
await new Task(() => Thread.Sleep(TimeSpan.FromSeconds(5)));
Debug.WriteLine("Finished Task1");
}
public static async Task Task2()
{
await new Task(() => Thread.Sleep(TimeSpan.FromSeconds(10)));
Debug.WriteLine("Finished Task2");
}
}
}
This program just blocks on the call to Task.WaitAll() and never finishes, but I am not understanding why. I'm sure I'm just missing something simple or just don't have the right mental model of this, and none of the blogs or MSDN articles that are out there are helping.
I recommend you start out with my intro to async/await and follow-up with the official Microsoft documentation on TAP.
As I mention in my intro blog post, there are several Task members that are holdovers from the TPL and have no use in pure async code. new Task and Task.Start should be replaced with Task.Run (or TaskFactory.StartNew). Similarly, Thread.Sleep should be replaced with Task.Delay.
Finally, I recommend that you do not use Task.WaitAll; your Console app should just Wait on a single Task which uses Task.WhenAll. With all these changes, your code would look like:
class Program
{
static void Main(string[] args)
{
MainAsync().Wait();
}
public static async Task MainAsync()
{
Task task1 = Task1();
Task task2 = Task2();
await Task.WhenAll(task1, task2);
Debug.WriteLine("Finished main method");
}
public static async Task Task1()
{
await Task.Delay(5000);
Debug.WriteLine("Finished Task1");
}
public static async Task Task2()
{
await Task.Delay(10000);
Debug.WriteLine("Finished Task2");
}
}
Understand C# Task, async and await
C# Task
Task class is an asynchronous task wrapper. Thread.Sleep(1000) can stop a thread running for 1 second. While Task.Delay(1000) won't stop the current work. See code:
public static void Main(string[] args){
TaskTest();
}
private static void TaskTest(){
Task.Delay(5000);
System.Console.WriteLine("task done");
}
When running," task done" will show up immediately. So I can assume that every method from Task should be asynchronous. If I replace TaskTest () with Task.Run(() =>TaskTest()) task done won't show up at all until I append a Console.ReadLine(); after the Run method.
Internally, Task class represent a thread state In a State Machine. Every state in state machine have several states such as Start, Delay, Cancel, and Stop.
async and await
Now, you may wondering if all Task is asynchronous, what is the purpose of Task.Delay ? next, let's really delay the running thread by using async and await
public static void Main(string[] args){
TaskTest();
System.Console.WriteLine("main thread is not blocked");
Console.ReadLine();
}
private static async void TaskTest(){
await Task.Delay(5000);
System.Console.WriteLine("task done");
}
async tell caller, I am an asynchronous method, don't wait for me. await inside the TaskTest() ask for waiting for the asynchronous task. Now, after running, program will wait 5 seconds to show the task done text.
Cancel a Task
Since Task is a state machine, there must be a way to cancel the task while task is in running.
static CancellationTokenSource tokenSource = new CancellationTokenSource();
public static void Main(string[] args){
TaskTest();
System.Console.WriteLine("main thread is not blocked");
var input=Console.ReadLine();
if(input=="stop"){
tokenSource.Cancel();
System.Console.WriteLine("task stopped");
}
Console.ReadLine();
}
private static async void TaskTest(){
try{
await Task.Delay(5000,tokenSource.Token);
}catch(TaskCanceledException e){
//cancel task will throw out a exception, just catch it, do nothing.
}
System.Console.WriteLine("task done");
}
Now, when the program is in running, you can input "stop" to cancel the Delay task.
Your tasks never finish because they never start running.
I would Task.Factory.StartNew to create a task and start it.
public static async Task Task1()
{
await Task.Factory.StartNew(() => Thread.Sleep(TimeSpan.FromSeconds(5)));
Debug.WriteLine("Finished Task1");
}
public static async Task Task2()
{
await Task.Factory.StartNew(() => Thread.Sleep(TimeSpan.FromSeconds(10)));
Debug.WriteLine("Finished Task2");
}
As a side note, if you're really just trying to pause in a async method, there's no need to block an entire thread, just use Task.Delay
public static async Task Task1()
{
await Task.Delay(TimeSpan.FromSeconds(5));
Debug.WriteLine("Finished Task1");
}
public static async Task Task2()
{
await Task.Delay(TimeSpan.FromSeconds(10));
Debug.WriteLine("Finished Task2");
}
Async and await are markers which mark code positions from where control should resume after a task (thread) completes.
Here's a detail youtube video which explains the concept in a demonstrative manner http://www.youtube.com/watch?v=V2sMXJnDEjM
If you want you can also read this coodeproject article which explains the same in a more visual manner.
http://www.codeproject.com/Articles/599756/Five-Great-NET-Framework-4-5-Features#Feature1:-“Async”and“Await”(Codemarkers)
static void Main(string[] args)
{
if (Thread.CurrentThread.Name == null)
Thread.CurrentThread.Name = "Main";
Console.WriteLine(Thread.CurrentThread.Name + "1");
TaskTest();
Console.WriteLine(Thread.CurrentThread.Name + "2");
Console.ReadLine();
}
private async static void TaskTest()
{
Console.WriteLine(Thread.CurrentThread.Name + "3");
await Task.Delay(2000);
if (Thread.CurrentThread.Name == null)
Thread.CurrentThread.Name = "FirstTask";
Console.WriteLine(Thread.CurrentThread.Name + "4");
await Task.Delay(2000);
if (Thread.CurrentThread.Name == null)
Thread.CurrentThread.Name = "SecondTask";
Console.WriteLine(Thread.CurrentThread.Name + "5");
}
If you run this program you will see that await will use different thread. Output:
Main1
Main3
Main2
FirstTask4 // 2 seconds delay
SecondTask5 // 4 seconds delay
But if we remove both await keywords, you will learn that async alone doesn't do much. Output:
Main1
Main3
Main4
Main5
Main2