Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 896
Álvaro García Avatar asked Apr 06 '12 11:04

Álvaro García


People also ask

Can a task return void?

Task return typeSuch methods return void if they run synchronously. If you use a Task return type for an async method, a calling method can use an await operator to suspend the caller's completion until the called async method has finished.

What is difference between task and void C#?

A Task returning async method can be awaited, and when the task completes, the continuation of the task is scheduled to run. A void returning async method cannot be awaited; it is a "fire and forget" method. It does work asynchronously, and you have no way of telling when it is done.

What happens when a method returns a task without awaiting it?

An exception that's raised in a method that returns a Task or Task<TResult> is stored in the returned task. If you don't await the task or explicitly check for exceptions, the exception is lost. If you await the task, its exception is rethrown. As a best practice, you should always await the call.

Can a void method be async?

It's easy to start several async void methods, but it's not easy to determine when they've finished. Async void methods will notify their SynchronizationContext when they start and finish, but a custom SynchronizationContext is a complex solution for regular application code. Async void methods are difficult to test.


2 Answers

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 */);
}
like image 62
svick Avatar answered Oct 06 '22 02:10

svick


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.

like image 32
Chris Benard Avatar answered Oct 06 '22 02:10

Chris Benard