Introduction
This article shows code and output of async / await calls side by side for easier comprehension. Idea in this sample is to call two functions, each having Task.Delay() call in it. This article will show how these two calls can be made sequentially using async/Task with the application being responsive.
About the demo application
It is a Form application in C#. There are four buttons towards the left on the application. Clicking on it would execute the code related to that functionality mentioned on the button. To the right of the buttons there is a text box where the executed code is shown and then there is a list box where the called functions print lines of text to demonstrate the flow. Screenshot of the UI is shown below for reference:
Note: All the code relevant to this article can be found in the file AsyncDemoForm.cs
async void
async void is a fire and forget mechanism. This is a good candidate for high level event handlers.
In function CallAsyncVoidButton_Click(), there are calls to two async void functions: WriteRandomLine() and WriteLine().
for (int i = 0; i < 10; i++)
{
WriteRandomLine();
WriteLine();
listBox.Items.Add("------------");
}
listBox.Items.Add("ASYNC VOID BUTTON CLICK FINISHED");
On running the sample it can be observed that, call to functions WriteRandomLine() and WriteLine() calls return immediately on hitting await inside those function calls since WriteRandomLine() and WriteLine() are async void functions. Once the control is returned back to CallAsyncVoidButton_Click() function, execution continues in that function. WriteRandomLine() and WriteLine() functions are shown below:
private async void WriteRandomLine()
{
Random rand = new Random();
int rand_num = rand.Next(1200, 2000);
await Task.Delay(rand_num);
listBox.Items.Add(string.Format("Random Line - delay = {0}ms, Thread Id = {1}", rand_num,
Thread.CurrentThread.ManagedThreadId));
}
private async void WriteLine()
{
await Task.Delay(1500);
listBox.Items.Add("Non-Random Line - delay = 1500ms, Thread Id = " + Thread.CurrentThread.ManagedThreadId);
}
async Task
In function CallAsyncTaskButton_Click(), there are calls to two async Task functions: WriteRandomLineAsync() and WriteLineAsync().
When await is hit inside WriteRandomLineAsync(), control returns back to the for loop, here WriteRandomLineAsync() has await applied to it and since it returns a Task, control is given to the caller of CallAsyncTaskButton_Click(). Execution is not continued further in CallAsyncTaskButton_Click() till the await at WriteRandomLineAsync() is completed. Once WriteRandomLineAsync() is finished, WriteLineAsync() gets called. Here also, the flow is similar to WriteRandomLineAsync() since await-s are applied in the same way.
private async void CallAsyncTaskButton_Click(object sender, EventArgs e)
{
listBox.Items.Clear();
listBox.Items.Add(string.Format("Current Thread Id: {0}", Thread.CurrentThread.ManagedThreadId));
for (int i = 0; i < 10; i++)
{
await WriteRandomLineAsync();
await WriteLineAsync();
listBox.Items.Add("------------");
}
listBox.Items.Add("ASYNC BUTTON CLICK FINISHED");
}
private async Task WriteRandomLineAsync()
{
Random rand = new Random();
int rand_num = rand.Next(1200, 2000);
await Task.Delay(rand_num);
listBox.Items.Add(string.Format("Random Line - delay = {0}ms, Thread Id = {1}", rand_num, Thread.CurrentThread.ManagedThreadId));
}
private async Task WriteLineAsync()
{
await Task.Delay(1500);
listBox.Items.Add("Non-Random Line - delay = 1500ms, Thread Id = " + Thread.CurrentThread.ManagedThreadId);
}
In this case, the results are printed in listbox in the order of call, but at the same time, UI is responsive.
Click on the Sychronous Call button to find out the difference, in that case UI would not be responsive till all function calls are completed.
Synchronous call
This is normal synchronous call and has been added to demonstrate the way UI becomes non-responsive. When this code is executing, trying to move the windows around does not respond as expected.
private void SynchronousCallButton_Click(object sender, EventArgs e)
{
listBox.Items.Clear();
for (int i = 0; i < 10; i++)
{
SynchronousWait10ms();
SynchronousWait20ms();
listBox.Items.Add("------------");
}
}
private void SynchronousWait10ms()
{
Thread.Sleep(200);
listBox.Items.Add("Wait 10 ms, Thread Id = " + Thread.CurrentThread.ManagedThreadId);
}
private void SynchronousWait20ms()
{
Thread.Sleep(300);
listBox.Items.Add("Wait 20 ms, Thread Id = " + Thread.CurrentThread.ManagedThreadId);
}
Task.Start
Here call is made to start the task which would run on a different thread. This thread would get assigned from the thread pool. Here also UI is responsive but it needs costly thread resources for a non CPU intensive code.
private void TaskStartButton_Click(object sender, EventArgs e)
{
listBox.Items.Clear();
Task demoTask = new Task(DemoTask);
demoTask.Start();
EnableAllButtons();
}
private void DemoTask()
{
try
{
List<Task> tsk1 = new List<Task> ();
List<Task> tsk2 = new List<Task> ();
for (int i = 0; i < 10; i++)
{
tsk1.Add(new Task(TaskDemoWait100ms));
tsk2.Add(new Task(TaskDemoWaitWait200ms));
}
for (int i = 0; i < 10; i++)
{
tsk1[i].Start();
tsk2[i].Start();
tsk2[i].Wait();
listBox.BeginInvoke(new separatorDelegate(() =>
{ listBox.Items.Add("------------"); }));
}
}
catch (Exception ex)
{
string msg = ex.Message;
}
}
delegate void taskdelegate(int threadId);
delegate void separatorDelegate();
private void TaskDemoWait100ms()
{
Thread.Sleep(100);
object[] param = new object[1];
param[0] = (object)Thread.CurrentThread.ManagedThreadId;
listBox.BeginInvoke(new taskdelegate((int threadId) =>
{ listBox.Items.Add("Wait 100 ms, Thread Id = " + threadId); }), param);
}
private void TaskDemoWaitWait200ms()
{
Thread.Sleep(200);
object[] param = new object[1];
param[0] = (object)Thread.CurrentThread.ManagedThreadId;
listBox.BeginInvoke(new taskdelegate((int threadId) =>
{ listBox.Items.Add("Wait 200 ms, Thread Id = " + threadId); }), param);
}
Environment
Developed in Visual Studio 2013 and tested on Windows 8.1 with update 2.
Some interesting links
http://www.codeproject.com/Articles/127291/C-vNext-New-Asynchronous-Pattern
http://channel9.msdn.com/Series/Three-Essential-Tips-for-Async/Three-Essential-Tips-For-Async-Introduction
http://www.jaylee.org/post/2012/07/08/c-sharp-async-tips-and-tricks-part-2-async-void.aspx)