When the method return void, is the same that a task?
Asked Answered
B

2

8

I am trying the async CTP, the versión 4.5 that allow use async methods without needing to write the Begin/End methods.

My first probe is to execute an async method that return void. I see a few examples and do the following:

private void btnAsync01_Click(object sender, RoutedEventArgs e)
{
    UpdateTxtLog("click button: " + System.DateTime.Now);
    method01Async();
    UpdateTxtLog("after ethod01Async: " + System.DateTime.Now);
}

private async void method01Async()
{
    await TaskEx.Run(() =>
    {
        UpdateTxtLog("Enter method01Async: " + System.DateTime.Now);
        Thread.Sleep(10000);
        UpdateTxtLog("exit method01Async: " + System.DateTime.Now);
    });
}

In my WPF project I have a textBox where to see the results and a button that execute the async method.

In the async method, I use await, that is need because the method is async, and the TasEx.Run to create a new thread in which execute the code.

My doubt is in this point. In a few examples that I see of how to create an async method that returns void, use this way, the Task.Run or TaskEx.Run.

If I am not wrong, Task.Run create a new thread where to execute the method. Then why to use an async method, if with the Task, creating a new thread, I get what I want, not to block the main thread?

Also, if the async method access some shared variable, I must be careful with the concurrency, right? So I don't know the advantage of using async methods, at least in this case.

In fact, I use the same code without async and without await and the result is the same, the main program is not blocking and all works as I expect. The method is this:

private void method01Async()
{
    TaskEx.Run(() =>
    {
        UpdateTxtLog("Enter method01Async: " + System.DateTime.Now);
        Thread.Sleep(10000);
        UpdateTxtLog("Exit method01Async: " + System.DateTime.Now);
    });
}

My question is, this is the correct way to use async when the method return void?

Boatel answered 6/4, 2012 at 11:37 Comment(4)
BTW, The Visual Studio 11 Beta is now out, which contains the features from the Async CTP, with some improvements and bug-fixes.Elayne
And which is the benefit os using async method in this case, if in the method I am using a task, and the task use a niew thread? If I use directly a task, without using an async method, I get the the behavior. Perhaps async methods have more sense if the method return a value and for void methods it's better use directly a task?Persinger
I strongly recommend that all async methods return Task or Task<TResult> unless they're event handlers and have to return void. This enables you to compose them if necessary, and error handling is cleaner, too (if an async void method raises an exception in a WPF context, it gets sent directly to the UI message loop, so async void methods are not truly "fire and forget").Coinage
Also, use Task.Run only to run code on the thread pool. You can use Task.Delay for an asynchronous wait instead of queuing a blocking Sleep.Coinage
E
4

If I am not wrong, Task.Run create a new thread where to execute the method.

Not exactly. Task.Run() will run the code on a thread different from the UI thread (at least with the default TaskScheduler). But it will not actually create a new thread in most cases, it will reuse an existing thread from the ThreadPool.

Then why to use an async method, if with the Task, creating a new thread, I get what I want, not to block the main thread?

The point of async, in the context of a UI application, is to be able to easily execute some code on the UI thread after and asynchronous operation completes.

So, if you made your method01Async “awaitable”, that is, made it return a Task:

private async Task method01Async()
{
    await Task.Run(/* whatever */);
}

You could then await it from the btnAsync01_Click method, if you made it `async:

private async void btnAsync01_Click(object sender, RoutedEventArgs e)
{
    UpdateTxtLog("click button: " + System.DateTime.Now);
    await method01Async();
    UpdateTxtLog("after method01Async: " + System.DateTime.Now);
}

This way, the last line of the method will execute only after the Task in method01Async finishes executing. And it will execute on the UI thread.

In .Net 4.0, you could achieve similar effect using ContinueWith() and Dispatcher.Invoke():

private void btnAsync01_Click(object sender, RoutedEventArgs e)
{
    UpdateTxtLog("click button: " + System.DateTime.Now);
    method01Async().ContinueWith(() =>
        Dispatcher.Invoke(
            new Action(() =>
                UpdateTxtLog("after method01Async: " + System.DateTime.Now)));
}

I'm sure you'll agree this is much messier and less readable.

Also, if the async method access some shared variable, I must be careful with the concurrency, right?

Yes, you're right about that.

In fact, I use the same code without async and without await and the result is the same, the main program is not blocking and all works as I expect.

The result certainly is not what I thought your code is supposed to do. The last line of btnAsync01_Click, will execute “after method01Async”, but it will not wait until the Task started in that method finishes.


As a side note, there is no need to use async in your method01Async. Returning the Task directly (or not, if you want to keep it void-returning), will work the same:

private Task method01Async()
{
    return Task.Run(/* whatever */);
}
Elayne answered 6/4, 2012 at 14:34 Comment(3)
Well, when I said that Task create a new thread, really I was simplifying, I know that Task use an existing thread from the threadpool, and if there is anyone free, then the task must wait. In m case, I don't want to await in the main method (click event in this case) because the secondary method (method01Async) only make something that not be notify to the main method. For example, send an email to something. I only want to send, but not to have care or notify in the main application if the operation was made or not.Persinger
Is what I am thinking, in void methods, perhaps async is not needed, and the async methods is recommended for methods that return a value.Persinger
If you don't want to do anything after the operations completes, then yes, there is no reason to use await. Although it might make sense to use inside email-sending code, to use the thread pool more efficiently.Elayne
M
1

You aren't really using the async in either case, since you're not awaiting the original call. Here is how you should be doing it:

private async void btnAsync01_Click(object sender, RoutedEventArgs e)
{
    UpdateTxtLog("click button: " + System.DateTime.Now);
    await method01Async();
    UpdateTxtLog("after ethod01Async: " + System.DateTime.Now);
}

private async Task method01Async()
{
    return await TaskEx.Run(() =>
    {
        UpdateTxtLog("Enter method01Async: " + System.DateTime.Now);
        Thread.Sleep(10000);
        UpdateTxtLog("exit method01Async: " + System.DateTime.Now);
    });
}

Once you change it to this (the important part being await method01Async() in your button click event, it will jump back there after it exits, and your "after ethod01Async: " text log should show a ten second delay, just like your "exit method01Async" log in the method01Async method.

Muckraker answered 6/4, 2012 at 12:53 Comment(4)
This won't compile. You need to make btnAsync01_Click async if you want to use await in it.Elayne
Yes, it's needed to make click event async. But with this way, I have the same doubt, if in the method01Async I use a task, I am using a new thread, so I don't see the benefit of using async. That's make me think that if the async methods have more sense when the method return a result and for void method perhaps it is better use directly a thread using a task.Persinger
@Elayne is right. I didn't actually compile this so I am going from memory. The compiler would quickly tell you that you can't call await from a method not tagged with async. Edited to reflect that. Thanks svick.Muckraker
@Daimroc, the reason to use async/await is that you want something done when it returns, but not before. In this case, your UpdateTxtLog("after ethod01Async: " + System.DateTime.Now); would run immediately if you don't await the return of the Task. With my example, it would only run after the Task completes, and it still happens on another thread and it is not blocking your UI thread.Muckraker

© 2022 - 2024 — McMap. All rights reserved.