I am new to async/await in C#. This is what I'm trying to do:
private async Task LoadSomeData()
{
/*
*This is where the 'Data' is loaded with HTTP
*/
//Data loading finished. Need to return control to next line from caller function and use this 'Data'
// Also need to use 'Data' to load 'Data2' over HTTP but is not very important
// and hence can be run in background. No piece of code depends on this currently.
}
private async void UIEventHandler()
{
//await until 'Data2' has loaded
//Continue handling the event after Data2 is loaded
}
My question is, how do I achieve the above operation. I don't want to return from LoadSomeData() method after retrieving Data2 as the current operation does not depend on it.
Below is some quick code I threw together around your method names. I've made them static just for convenience.
using System;
using System.Threading;
using System.Threading.Tasks;
public class Program
{
public static void Main()
{
Console.WriteLine("Main Start");
UIEventHandler(2000);
UIEventHandler(500);
UIEventHandler(1000);
Console.WriteLine("Main Stop");
Thread.Sleep(3000); // wait for tasks to finish
}
private static async Task LoadSomeData(int delay)
{
await Task.Run(() =>
{
Console.WriteLine("Start (LoadSomeData) " + delay);
Thread.Sleep(delay);
Console.WriteLine("Stop (LoadSomeData) " + delay);
});
}
private static async Task UIEventHandler(int delay)
{
Console.WriteLine("Start (UIEventHandler) " + delay);
await LoadSomeData(delay);
Console.WriteLine("Stop (UIEventHandler) " + delay);
}
}
This is sample output from a test run:
Main Start
Start (UIEventHandler) 2000
Start (LoadSomeData) 2000
Start (UIEventHandler) 500
Start (UIEventHandler) 1000
Main Stop
Start (LoadSomeData) 500
Start (LoadSomeData) 1000
Stop (LoadSomeData) 500
Stop (UIEventHandler) 500
Stop (LoadSomeData) 1000
Stop (UIEventHandler) 1000
Stop (LoadSomeData) 2000
Stop (UIEventHandler) 2000
You will notice that every call to UIEventHandler showed it's "Start" message straight away. However, the "Stop" message is not shown until the respective call to LoadSomeData is complete. The await operator is essentially telling the code in UIEventHandler to stop until the task in the called method is complete.
That's a really simplistic view of it, though. I'd suggest doing some reading. This might be a good start:
Asynchronous programming
private async Task LoadSomeData()
{
var data = await Task.Delay(1000); // Replace with your HTTP GET
return data;
}
private async Task LoadSomeData2(Data data)
{
var data2 = await Task.Delay(1000); // Replace with your other HTTP GET
return data2;
}
private async void UIEventHandler()
{
var data = await LoadSomeData();
var data2 = await LoadSomeData2(data);
}
UPDATE:
I recommend you read Async/Await - Best Practices in Asynchronous Programming
Related
This question already has answers here:
Adding a short delay inside a loop prevents it from looping indefinitely. Why?
(2 answers)
Why does Console.In.ReadLineAsync block?
(3 answers)
Closed 1 year ago.
I want to make a C# Console App, that can evaluate user input while it is doing some work. For that I want to await the Input asynchron similar to this: await Console.ReadLine(). For testing purpose I simply want the main work loop to stop running when I hit Enter. I have achieved that like this:
using System;
using System.Threading;
using System.Threading.Tasks;
class WorkTillEnter
{
private static bool running = false;
public static void Main(string[] args)
{
WorkTillEnter.running = true;
WorkTillEnter.observeInputAsync();
DateTime lastTick = DateTime.Now;
while(WorkTillEnter.running){
if(lastTick.Second != DateTime.Now.Second){
Console.WriteLine($"Tick {DateTime.Now}");
lastTick = DateTime.Now;
}
//Doing Work in this loop until enter is hit
}
Console.WriteLine("Worker Terminated.");
}
private static async void observeInputAsync(){
await Task.Delay(1); // <--WHY???
await Console.In.ReadLineAsync();
WorkTillEnter.running = false;
}
}
This works fine and prints Ticks every Second until Enter is hit. My Qustion is now: Why does it not work when I delete this one Line? await Task.Delay(1); // <--WHY??? Whithout this Line the programm does nothing until I hit return, and then obviously never enters the while loop. How can this behavior be explained?
This suggestion Why does Console.In.ReadLineAsync block? explaines why Console.In.ReadLineAsyncisn't behaving as expected when my questionable Line is removed. But it does not explain why it actualy beahaves as expected when the Line is added.
Here await Task.Delay(1); making sure that your program enters into the while loop. If you don't use this, the function waits for an input. When the input is given, the boolean value of running becomes false and the program never gets into the loop.
I don't know why on earth await Console.In.ReadLineAsync(); is blocking for an input. Maybe it's a bug. 🤷🏻♂️
A simple debugging process like this can help to understand the situation.
using System;
using System.Threading.Tasks;
class WorkTillEnter
{
private static bool running;
public static void Main(string[] args)
{
Console.WriteLine("a");
running = true;
Console.WriteLine("b");
ObserveInputAsync();
Console.WriteLine("c");
DateTime lastTick = DateTime.Now;
Console.WriteLine("d");
while (running)
{
Console.WriteLine("e");
if (lastTick.Second != DateTime.Now.Second)
{
Console.WriteLine($"Tick {DateTime.Now}");
lastTick = DateTime.Now;
}
//Doing Work in this loop until enter is hit
}
Console.WriteLine("f");
Console.WriteLine("Worker Terminated.");
}
private static async void ObserveInputAsync()
{
Console.WriteLine("1");
await Task.Delay(1); // <--WHY???
Console.WriteLine("2");
await Console.In.ReadLineAsync();
running = false;
Console.WriteLine("3");
}
}
Thx to Andreas Huber for giving the reason. Console.In.ReadLineAsync() is bugged and actualy blocking until Input is submited. But the questionable Line pushes the async function to a new Thread. Testing the code like this:
using System;
using System.Threading;
using System.Threading.Tasks;
class WorkTillEnter
{
private static bool running = false;
public static void Main(string[] args)
{
WorkTillEnter.running = true;
Console.WriteLine($"before Asnyc called: {Thread.CurrentThread.ManagedThreadId}");
WorkTillEnter.observeInputAsync();
Console.WriteLine($"after Asnyc called: {Thread.CurrentThread.ManagedThreadId}");
DateTime lastTick= DateTime.Now;
while(WorkTillEnter.running){
if(lastTick.Second != DateTime.Now.Second){
Console.WriteLine($"Tick {DateTime.Now}");
lastTick = DateTime.Now;
}
//Doing Work in this loop until enter is hit
}
Console.WriteLine("Worker Terminated.");
}
private static async void observeInputAsync(){
Console.WriteLine($"Async before await: {Thread.CurrentThread.ManagedThreadId}");
await Task.Delay(1); // <--WHY???
Console.WriteLine($"Async after first await: {Thread.CurrentThread.ManagedThreadId}");
await Console.In.ReadLineAsync();
Console.WriteLine($"Async after second await: {Thread.CurrentThread.ManagedThreadId}");
WorkTillEnter.running = false;
}
}
results in output: (you can see at which point i hit return)
before Asnyc called: 1
Async before await: 1
after Asnyc called: 1
Async after first await: 4
Tick 11.05.2021 20:30:42
Tick 11.05.2021 20:30:43
Async after second await: 4
Worker Terminated.
but with the line removed gives output like this:
before Asnyc called: 1
Async before await: 1
Async after first await: 1
Async after second await: 1
after Asnyc called: 1
Worker Terminated.
ok. I made a simple console app to figure out how to make all this work. Once I have the basic outline working, then I'll apply it to the real application.
The idea is that we have a lot of database calls to execute that we know are going to take a long time. We do NOT want to (or have to) wait for one database call to be completed before we make the next. They can all run at the same time.
But, before making all of the calls, we need to perform a "starting" task. And when all of the calls are complete, we need to perform a "finished" task.
Here's where I'm at now:
static void Main(string[] args)
{
Console.WriteLine("starting");
PrintAsync().Wait();
Console.WriteLine("ending"); // Must not fire until all tasks are finished
Console.Read();
}
// Missing an "await", I know. But what do I await for?
static async Task PrintAsync()
{
Task.Run(() => PrintOne());
Task.Run(() => PrintTwo());
}
static void PrintOne()
{
Console.WriteLine("one - start");
Thread.Sleep(3000);
Console.WriteLine("one - finish");
}
static void PrintTwo()
{
Console.WriteLine("two - start");
Thread.Sleep(3000);
Console.WriteLine("two - finish");
}
But no matter what I try, Ending always gets printed too early:
starting
ending
one - start
two - start
one - finish
two - finish
What IS working right is that PrintTwo() starts before PrintOne() is done. But how do I properly wait for PrintAsync() to finish before doing anything else?
you need to await the ending of the inner tasks:
static async Task PrintAsync()
{
await Task.WhenAll(Task.Run(() => PrintOne()), Task.Run(() => PrintTwo()));
}
explanation: async Task denotes an awaitable method. Inside this method you can also await Tasks. If you don't do this then it will simply let the tasks loose which will run on their own. Task.Run returns a Task which can be awaited. If you want both tasks to run in parallel you can use the tasks from the retur values and use them in the awaitable method Task.WhenAll
EDIT: Actually Visual Studio would mark this code with a green curvy line. When hoovering with the mouse over it you get a warning:
CS4014
This should explain why "ending" is printed before the tasks have finished
EDIT 2:
If you have a collection of parameters that you want to iterate and call an async method to pass the parameter in, you can also do it with a select statement in 1 line:
static async Task DatabaseCallsAsync()
{
// List of input parameters
List<int> inputParameters = new List<int> {1,2,3,4,5};
await Task.WhenAll(inputParameters.Select(x => DatabaseCallAsync($"Task {x}")));
}
static async Task DatabaseCallAsync(string taskName)
{
Console.WriteLine($"{taskName}: start");
await Task.Delay(3000);
Console.WriteLine($"{taskName}: finish");
}
The last part is similar to a previous answer
OP here. I'm going to leave the answer by Mong Zhu marked as correct, as it lead me to the solution. But I also want to share the final result here, which includes excellent feedback in the comments from juharr. Here's what I came up with:
class Program
{
static void Main(string[] args)
{
Console.WriteLine("starting");
DatabaseCallsAsync().Wait();
Console.WriteLine("ending"); // Must not fire until all database calls are complete.
Console.Read();
}
static async Task DatabaseCallsAsync()
{
// This is one way to do it...
var tasks = new List<Task>();
for (int i = 0; i < 3; i++)
{
tasks.Add(DatabaseCallAsync($"Task {i}"));
}
await Task.WhenAll(tasks.ToArray());
// This is another. Same result...
List<int> inputParameters = new List<int> { 1, 2, 3, 4, 5 };
await Task.WhenAll(inputParameters.Select(x => DatabaseCallAsync($"Task {x}")));
}
static async Task DatabaseCallAsync(string taskName)
{
Console.WriteLine($"{taskName}: start");
await Task.Delay(3000);
Console.WriteLine($"{taskName}: finish");
}
}
Here's the result:
starting
Task 0: start
Task 1: start
Task 2: start
Task 2: finish
Task 0: finish
Task 1: finish
ending
I have four methods.
Main: only calls the preform method
Working: displays "please wait for the user"
Taking Time: A program that takes time to execute.
Preform: Calls the taking time and working methods asynchronously.
The following is my code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace AsyncObservation
{
class Program
{
static void Main(string[] args)
{
preform();
}
public static async Task Working()
{
Console.WriteLine("Please wait, the program is running");
}
public static async Task Takingtime()
{
Console.WriteLine("This Program started");
Thread.Sleep(1000);
Console.WriteLine("The Program finished");
}
public static async void preform()
{
Task timer = Takingtime();
Task wait = Working();
}
}
}
In the end: I need to display
This program started.
Please wait, the program is running
The program ended.
I see several problems in your program.
Although Preform is neither async nor an event handler, it does not return a Task
The tasks started in Preform are not awaited for before you finish Preform. Hence you never know when they are finished, nor what the results are (exception?). You could even end your program before they are finished
After you start a Task it is not guaranteed when it will run. You can only be sure that statements are already executed if you await for the Task.
Using async-await is a method to make sure that your thread looks around to see if it can do useful stuff instead of waiting idly if it has to wait for something. Thread.Sleep is a busy wait. If you want to look around to see if you can do something else use await Task.Delay(TimeSpan.FromSeconds(1)) instead.
In your case, you can't be sure that any Console line has been written until you await the procedure that should write your line. If you start your second Task before awaiting the first, you don't know how far the first task already proceeded, and thus you don't know for sure that the text already has been written to the Console.
C# 7.1 introduced async Task Main(), so you could use that instead of the traditional void Main. It saves you from catching and interpreting the AggregateException that is thrown by the Task you start to make your process async.
If you don't want to use the async Main, you can of course use Task.Run to call an async function:
static void Main(string[] args)
{
try
{
var preformTask = Task.Run( () => Preform() );
DoSomethingElse(); // if needed
preformTask.Wait(); // wait for preformTask to finish
Console.WriteLine("Task completed; press any key to finish");
Console.ReadKey();
}
catch (Exception exc) // inclusive ggregateException if one of your Task fails
{
ProcessException(exc)
}
}
static async Task preform()
{
// To be certain that the Console Line has been written: await
await Takingtime();
// if here, you are certain that the Line has been written,
// or course you have lost parallel processing
await Working();
}
For completeness: the other functions
public static async Task Working()
{
Console.WriteLine("Please wait, the program is running");
// either return a completed Task, or await for it (there is a difference!
await Task.CompletedTask;
// or:
return Task.CompletedTask; // do not declare async in this case
}
public static async Task Takingtime()
{
Console.WriteLine("This Program started");
//Use Task.Delay instead of Sleep
await Task.Delay(TimeSpan.FromSeconds(1); // improved readability
Console.WriteLine("The Program finished");
}
Because of the awaits in Preform you are certain that the text has been written. However, you've lost some parallellism.
If you really want those procedures to execute at the same time, you can't be certain about when text will be written. If that is important, then split the Parts that should be run first (write Console) from the parts that should run in parallel (Task.Delay)
static async Task preform()
{
// Do the things that should be done before parallel tasks are run
await DoThisFirst();
// start the Tasks that can work parallel: not sure what statements are executed first
var taskA = DoTaskA();
var taskB = DoTaskB();
// if here, you are free to do something else
// can't be sure about the status of taskA nor taskB
DoSomethingElse();
// if you want to do something after you know that the tasks have completed:
// await the tasks here:
await Task.When (new Task[] {taskA, taskB});
// if here, you are certain that all parallel tasks have completed successfully
// if desired fetch the return values of the Tasks:
var returnValueA = taskA.Result;
var returnValueB = taskB.Result;
// do all async things of which you needed to be certain that both tasks finished
// for example:
await ProcessResults(returnValueA, returnValueB);
}
In a console app it is OK to use a .Wait() call in an void Main method.
In some contexts where there is synchronisation necessary .Wait() can cause deadlocks (ASP.NET has a request to synchronise on or XAML/WinForms which have a UI thread), but there is nothing to synchronise on here.
static void Main()
{
preform.Wait()
}
This will wait for the async work to complete synchronously. You need the method to run synchronously so that it does not return early. If you use async void and await the method will return immediately and exit the method, await does not work in a Main method.
I would also suggest using await Task.Delay(1000); rather than Thread.Sleep(1000); as this is the canonical async way to idle.
For your code example:
class Program
{
static void Main(string[] args)
{
preform().Wait();
Console.ReadLine();
}
public static async Task Working()
{
Console.WriteLine("Please wait, the program is running");
}
public static async Task Takingtime()
{
Console.WriteLine("This Program started");
await Task.Delay(1000);
Console.WriteLine("The Program finished");
}
public static Task preform()
{
return Task.WhenAll(
Takingtime(),
Working());
}
}
Use Stephen Cleary's Nito.AsyncEx library (available via Nuget) to provide an asynchronous context for a console application. See here for more details.
Your application can then be written as ...
class Program
{
static int Main(string[] args)
{
try
{
Console.WriteLine("The application has started");
AsyncContext.Run(() => LongRunningTaskAsync(args));
Console.WriteLine("The application has finished");
}
catch (Exception ex)
{
Console.Error.WriteLine(ex);
return -1;
}
}
static async Task LongRunningTaskAsync(string[] args)
{
Console.WriteLine("The long running task has started");
// use Task.Delay() rather than Thread.Sleep() to avoid blocking the application
await Task.Delay(TimeSpan.FromSeconds(10)).ConfigureAwait(false);
Console.WriteLine("The long running task has finished");
}
}
Use preform().Wait() in the constructor. So the call is awaited. Then use await Takingtime() and await Working() in the preform method.
And you have to change the return type to Task.
I may be missing something but what is the difference between doing:
public void MyMethod()
{
Task t = Task.Factory.StartNew(DoSomethingThatTakesTime);
t.Wait();
UpdateLabelToSayItsComplete();
}
public async void MyMethod()
{
var result = Task.Factory.StartNew(DoSomethingThatTakesTime);
await result;
UpdateLabelToSayItsComplete();
}
private void DoSomethingThatTakesTime()
{
Thread.Sleep(10000);
}
I may be missing something
You are.
what is the difference between doing Task.Wait and await task?
You order your lunch from the waiter at the restaurant. A moment after giving your order, a friend walks in and sits down next to you and starts a conversation. Now you have two choices. You can ignore your friend until the task is complete -- you can wait until your soup arrives and do nothing else while you are waiting. Or you can respond to your friend, and when your friend stops talking, the waiter will bring you your soup.
Task.Wait blocks until the task is complete -- you ignore your friend until the task is complete. await keeps processing messages in the message queue, and when the task is complete, it enqueues a message that says "pick up where you left off after that await". You talk to your friend, and when there is a break in the conversation the soup arrives.
To demonstrate Eric's answer here is some code:
public void ButtonClick(object sender, EventArgs e)
{
Task t = new Task.Factory.StartNew(DoSomethingThatTakesTime);
t.Wait();
//If you press Button2 now you won't see anything in the console
//until this task is complete and then the label will be updated!
UpdateLabelToSayItsComplete();
}
public async void ButtonClick(object sender, EventArgs e)
{
var result = Task.Factory.StartNew(DoSomethingThatTakesTime);
await result;
//If you press Button2 now you will see stuff in the console and
//when the long method returns it will update the label!
UpdateLabelToSayItsComplete();
}
public void Button_2_Click(object sender, EventArgs e)
{
Console.WriteLine("Button 2 Clicked");
}
private void DoSomethingThatTakesTime()
{
Thread.Sleep(10000);
}
This example demonstrates the difference very clearly. With async/await the calling thread will not block and continue executing.
static void Main(string[] args)
{
WriteOutput("Program Begin");
// DoAsTask();
DoAsAsync();
WriteOutput("Program End");
Console.ReadLine();
}
static void DoAsTask()
{
WriteOutput("1 - Starting");
var t = Task.Factory.StartNew<int>(DoSomethingThatTakesTime);
WriteOutput("2 - Task started");
t.Wait();
WriteOutput("3 - Task completed with result: " + t.Result);
}
static async Task DoAsAsync()
{
WriteOutput("1 - Starting");
var t = Task.Factory.StartNew<int>(DoSomethingThatTakesTime);
WriteOutput("2 - Task started");
var result = await t;
WriteOutput("3 - Task completed with result: " + result);
}
static int DoSomethingThatTakesTime()
{
WriteOutput("A - Started something");
Thread.Sleep(1000);
WriteOutput("B - Completed something");
return 123;
}
static void WriteOutput(string message)
{
Console.WriteLine("[{0}] {1}", Thread.CurrentThread.ManagedThreadId, message);
}
DoAsTask Output:
[1] Program Begin
[1] 1 - Starting
[1] 2 - Task started
[3] A - Started something
[3] B - Completed something
[1] 3 - Task completed with result: 123
[1] Program End
DoAsAsync Output:
[1] Program Begin
[1] 1 - Starting
[1] 2 - Task started
[3] A - Started something
[1] Program End
[3] B - Completed something
[3] 3 - Task completed with result: 123
Update: Improved example by showing the thread ID in the output.
Wait(), will cause to run potentially async code in sync manner. await will not.
For example, you have an asp.net web application. UserA calls /getUser/1 endpoint. asp.net app pool will pick a thread from thread pool (Thread1) and, this thread will make a http call. If you do Wait(), this thread will be blocked until http call resolves. While it is waiting, if UserB calls /getUser/2, then, app pool will need to serve another thread (Thread2) to make http call again. You just created (Well, fetched from app pool actually) another thread for no reason, because you cannot use Thread1 it was blocked by Wait().
If you use await on Thread1, then, SyncContext will manage sync between Thread1 and http call. Simply, it will notify once http call is done. Meanwhile, if UserB calls /getUser/2, then, you will use Thread1 again to make http call, because it was released once await got hit. Then another request can use it, even further more. Once http call is done (user1 or user2), Thread1 can get the result and return to caller (client). Thread1 was used for multiple tasks.
In this example, not much, practically. If you are awaiting a Task that returns on a different thread (like a WCF call) or relinquishes control to the operating system (like File IO), await will use fewer system resources by not blocking a thread.
In the example above, you can use "TaskCreationOptions.HideScheduler", and greatly modify the "DoAsTask" method. The method itself is not asynchronous, as it happens with "DoAsAsync" because it returns a "Task" value and is marked as "async", making several combinations, this is how it gives me exactly the same as using "async / await":
static Task DoAsTask()
{
WriteOutput("1 - Starting");
var t = Task.Factory.StartNew<int>(DoSomethingThatTakesTime, TaskCreationOptions.HideScheduler); //<-- HideScheduler do the magic
TaskCompletionSource<int> tsc = new TaskCompletionSource<int>();
t.ContinueWith(tsk => tsc.TrySetResult(tsk.Result)); //<-- Set the result to the created Task
WriteOutput("2 - Task started");
tsc.Task.ContinueWith(tsk => WriteOutput("3 - Task completed with result: " + tsk.Result)); //<-- Complete the Task
return tsc.Task;
}
I have a very simple task that loads some data like so:
async Task<List<Invoice>> GetInvoices()
{
var invoices = await Task.Run(() => db.Invoices.AsNoTracking().ToList());
return invoices;
}
What would be the best way to use this with a progressbar? I don't need to get the percentage loaded (although that would be useful), I simply need to show/hide the progressbar at start and finish of loading data. Is there a way to create a completed or finished method perhaps?
I am very new to async/await so kid gloves please!
Assuming GetInvoices gets called in some UI event handler and there is some progress bar control called ProgressBar, it's really simple:
private async void OnButton1Click(..)
{
ProgressBar.IsIndeterminate = true;
var result = await GetInvoices();
ProgressBar.IsIndeterminate = false; // Maybe hide it, too
// TODO: Do stuff with result
}
If you want to show actual progress, take a look at Progress<T> and IProgress<T>.
You already have what you need. To illustrate here's a simple example with tasks and using await, and ContinueWith.
using System;
using System.Threading.Tasks;
using System.Diagnostics;
public class Program
{
public static void Main()
{
Task t = new Program().TestTasks();
Console.WriteLine("hello");
t.Wait();
}
public async Task TestTasks()
{
Stopwatch watch = Stopwatch.StartNew();
Console.WriteLine("1." + watch.ElapsedMilliseconds);
// await task
int res = await TestAsync();
Console.WriteLine("2." + watch.ElapsedMilliseconds + " " + res);
// save task and wait on it
Task<int> t = TestAsync();
t.ContinueWith((r) =>
{
Console.WriteLine("4." + watch.ElapsedMilliseconds + " " + r.Result);
});
Console.WriteLine("3." + watch.ElapsedMilliseconds);
t.Wait();
}
public static async Task<int> TestAsync()
{
await Task.Delay(TimeSpan.FromSeconds(2));
return 42;
}
}
This prints:
1.0
hello
2.2009 42
3.2009
4.4013 42
Note how the await leaves the method and prints "hello" in the Main method, and after (about) 2 seconds continue in the TestTasks method. On the other hand, if we do not await, as the second call to TestAsync shows, 3. is printed immediately, and after 2 seconds, 4. is printed.
With this in mind, you can just call your method GetInvoices with await since you're returning a Task.
// Set state of progressbar to indeterminate
List<Invoice> result = await GetInvoices();
// Set state of progressbar to nothing
You can do something like:
var task = Task.Run(() => db.Invoices.AsNoTracking().ToList());
//show progress
task.ContinueWith(...CLOSE PROGRESS BAR...);
In practise you use Task.ContinueWith construct to catch the termination of the task and hide a progress bar you have shown before.
Note: keep in mind that acting on UI element from another thread requires Invoke() call, as that call redirects actual invokation to the main thread, which is a only thread were UI controls can be operated on windows.