Speerian
Speerian

Reputation: 1208

Clarification for Await/Async and Task

Recently I've started working with multithreading and have been trying to better understand await/async and Task. I've typed up the following scenario:

private void button1_Click(object sender, EventArgs e)
{
    var list = GenerateList();
    progressBar1.Maximum = list.Count ();

    CreateStuff (list));
    MessageBox.Show ("Complete!");
}

async void CreateStuff (List<int> list)
{
    //additional Parameters here used for DoWork()
    await Task.Run (()=>Parallel.ForEach(list, item =>
    {
        DoWork();
        Invoke(new Action(() => progressBar1.PerformStep()));
    }));
    //do something that relies on list
}

I'm trying to make the message box appear after the completion of the CreateDocs method. I was told by a peer that it was possible to keep using await async in the CreateStuff() method and still wait until the method is compelte before I show the message box. I've been researching await async and Task for a while and can't seem to figure out how to do this.

To me, it seems much easier to make the button1_Click() use await async, then await the CreateStuff() method.

My question is this: Is it possible to keep using await async in the CreateStuff method and still wait for the completion of the CreateStuff() method in button1_Click() before displaying the message box?

I'm assuming that it has to do with returning the Task, but I can't quite figure out how to progress about it.

Any help is appreciated. Thanks!

Upvotes: 1

Views: 208

Answers (2)

Jon Skeet
Jon Skeet

Reputation: 1503120

My question is this: Is it possible to keep using await async in the CreateStuff method and still wait for the completion of the CreateStuff() method in button1_Click() before displaying the message box?

Absolutely - but you need to change the return type of CreateStuff first. Currently you're returning void, which is almost never a good idea - it's really only there for event handlers. All you need to do is change it to return Task, and you can await it:

async Task CreateStuff (List<int> list)

Then in your click handler (which needs to be async too), await the result of calling it:

await CreateStuff (list);

It's not at all clear that the implementation of CreateStuff is really ideal, mind you. You don't need an async method for that - you could just have:

Task CreateStuff(List<int> list)
{
    return Task.Run(() => Parallel.ForEach(list, item =>
    {
        DoWork();
        Invoke(new Action(() => progressBar1.PerformStep()));
    }));
}

There's not a lot of point having an async method which exists just to create a task and await it, when you can just return the task and let the calling code await that instead. (They do behave slightly differently in a few ways, but not in any way which is important in this particular case, I suspect.)

Upvotes: 6

SLaks
SLaks

Reputation: 888047

Your problem is async void.

You need to change CreateStuff() to async Task, so that you can await it at the callsite.

You should only use async void for event handlers.

Upvotes: 2

Related Questions