Click here to Skip to main content
15,867,307 members
Articles / Programming Languages / C#
Tip/Trick

async / await example

Rate me:
Please Sign up or sign in to vote.
3.62/5 (7 votes)
13 Jul 2014CPOL2 min read 31.1K   1.1K   26   6
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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
India India

Comments and Discussions

 
SuggestionNice little demo Pin
OneWinsto10-Jul-15 12:28
OneWinsto10-Jul-15 12:28 
A nice little demo for playing with async. I did something similar myself, but a simpler console app, when first investigating the await/async stuff in C# 5.0 and I didn't go to the trouble of writing it up on here, so good work on that Blush | :O

There is a slight glitch in this logic :-

// If the value in rand_num is less than 1500, then "Random Line" gets printed
// otherwise "Non-Random Line" gets printed

though, I think. Because the addition to the listBox.Items happens inside each async method both the random and the non random entries will be (in fact do get) added. I can see what you are trying to do though.

In order to achieve your desired effect you'd need to return the items to be added as a task result (or indeed return a Func that adds the items as a Task result). Then do something like :-

C#
var itemToAdd = await Task.WhenAny(WriteRandomLineAsync(), WriteLineAsync());

listBox.Items.Add(itemToAdd);


where WriteRandomLineAsync() and WriteLineAsync() both return a
C#
Task<string>


or
C#
await Task.WhenAny(WriteRandomLineAsync(), WriteLineAsync())();

with the methods returning
C#
Task<Action>
if you do the Func to add to listBox thing.


does that make sense? Blush | :O )
GeneralRe: Nice little demo Pin
Sharath C V11-Jul-15 1:57
professionalSharath C V11-Jul-15 1:57 
GeneralRe: Nice little demo Pin
OneWinsto11-Jul-15 8:59
OneWinsto11-Jul-15 8:59 
QuestionHow can...? Pin
Christian Amado14-Jul-14 2:30
professionalChristian Amado14-Jul-14 2:30 
QuestionRe: How can...? Pin
OneWinsto10-Jul-15 12:33
OneWinsto10-Jul-15 12:33 
NewsBroken image links Pin
Sharath C V14-Jul-14 1:33
professionalSharath C V14-Jul-14 1:33 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.