Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

async / await example

0.00/5 (No votes)
14 Jul 2014 4  
A simple async / await example

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);
    // If the value in rand_num is less than 1500, then "Random Line" gets printed
    // otherwise "Non-Random Line" gets printed
    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)
{
    // clear the list before writing
    listBox.Items.Clear();
    listBox.Items.Add(string.Format("Current Thread Id: {0}", Thread.CurrentThread.ManagedThreadId));
    for (int i = 0; i < 10; i++)
    {
        // comment the await in below two lines and it would behave the same way as 
        // when async void button Click() gets called
        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);
    // If the value in rand_num is less than 1500, then "Random Line" gets printed
    // otherwise "Non-Random Line" gets printed
    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)
{
    // clear the list before writing
    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)
{
    // clear the list before writing
    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)

 

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here