What's the Difference Between Returning Void and Returning a Task

What's the difference between returning void and returning a Task?

SLaks and Killercam's answers are good; I thought I'd just add a bit more context.

Your first question is essentially about what methods can be marked async.

A method marked as async can return void, Task or Task<T>. What are the differences between them?

A Task<T> returning async method can be awaited, and when the task completes it will proffer up a T.

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. This is more than a little bit weird; as SLaks says, normally you would only do that when making an asynchronous event handler. The event fires, the handler executes; no one is going to "await" the task returned by the event handler because event handlers do not return tasks, and even if they did, what code would use the Task for something? It's usually not user code that transfers control to the handler in the first place.

Your second question, in a comment, is essentially about what can be awaited:

What kinds of methods can be awaited? Can a void-returning method be awaited?

No, a void-returning method cannot be awaited. The compiler translates await M() into a call to M().GetAwaiter(), where GetAwaiter might be an instance method or an extension method. The value awaited has to be one for which you can get an awaiter; clearly a void-returning method does not produce a value from which you can get an awaiter.

Task-returning methods can produce awaitable values. We anticipate that third parties will want to create their own implementations of Task-like objects that can be awaited, and you will be able to await them. However, you will not be allowed to declare async methods that return anything but void, Task or Task<T>.

(UPDATE: My last sentence there may be falsified by a future version of C#; there is a proposal to allow return types other than task types for async methods.)

(UPDATE: The feature mentioned above made it in to C# 7.)

async/await - when to return a Task vs void?

  1. Normally, you would want to return a Task. The main exception should be when you need to have a void return type (for events). If there's no reason to disallow having the caller await your task, why disallow it?

  2. async methods that return void are special in another aspect: they represent top-level async operations, and have additional rules that come into play when your task returns an exception. The easiest way is to show the difference is with an example:

static async void f()
{
await h();
}

static async Task g()
{
await h();
}

static async Task h()
{
throw new NotImplementedException();
}

private void button1_Click(object sender, EventArgs e)
{
f();
}

private void button2_Click(object sender, EventArgs e)
{
g();
}

private void button3_Click(object sender, EventArgs e)
{
GC.Collect();
}

f's exception is always "observed". An exception that leaves a top-level asynchronous method is simply treated like any other unhandled exception. g's exception is never observed. When the garbage collector comes to clean up the task, it sees that the task resulted in an exception, and nobody handled the exception. When that happens, the TaskScheduler.UnobservedTaskException handler runs. You should never let this happen. To use your example,

public static async void AsyncMethod2(int num)
{
await Task.Factory.StartNew(() => Thread.Sleep(num));
}

Yes, use async and await here, they make sure your method still works correctly if an exception is thrown.

For more information see: https://learn.microsoft.com/en-us/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming

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

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 */);
}

Do I need to return anything from an async task?

As a rule, when a non-async method returns void, its async counterpart should return Task:

public static async Task PopulateMetrics()

According to Microsoft, you should use void return from async methods only when you implement an event handler:

Void-returning async methods have a specific purpose: to make asynchronous event handlers possible. It is possible to have an event handler that returns some actual type, but that doesn't work well with the language; invoking an event handler that returns a type is very awkward, and the notion of an event handler actually returning something doesn't make much sense. Event handlers naturally return void, so async methods return void so that you can have an asynchronous event handler.

Is there a difference with Task.Run a void method and Task method returning null?

You should never return a null task; that should cause a runtime NullReferenceException error.

You can use await within an async void method, but you cannot use await to consume an async void method (because you cannot await void).

I recommend that you review my async intro blog post; it should help you get a better understanding of async and await.

am I disposing CancellationTokenSource correctly here?

Your start button needs to cancel/dispose the old cts when it creates a new one.

Difference between Task and void methods (Non Async)

public Task DoWork() {} can be awaited in an async call

public void DoWork() {} don't

Correct way of returning a task in C#

If you're asking should I expose a property or method, then that entirely depends on what the Task represents.

If the task is something done once per instance of the class, then having a Task property is appropriate. Usually in this case, the property represents something about the instance, such as "my initialization is complete" or "I am done processing".

If the task is something that you need to do multiple times, then having a Task-returning method is appropriate.

Task-returning methods are vastly more common than Task properties.

On a side note, avoid async void and don't use Task.Run unnecessarily.



Related Topics



Leave a reply



Submit