Await Task.Run VS Await

When correctly use Task.Run and when just async-await

Note the guidelines for performing work on a UI thread, collected on my blog:

  • Don't block the UI thread for more than 50ms at a time.
  • You can schedule ~100 continuations on the UI thread per second; 1000 is too much.

There are two techniques you should use:

1) Use ConfigureAwait(false) when you can.

E.g., await MyAsync().ConfigureAwait(false); instead of await MyAsync();.

ConfigureAwait(false) tells the await that you do not need to resume on the current context (in this case, "on the current context" means "on the UI thread"). However, for the rest of that async method (after the ConfigureAwait), you cannot do anything that assumes you're in the current context (e.g., update UI elements).

For more information, see my MSDN article Best Practices in Asynchronous Programming.

2) Use Task.Run to call CPU-bound methods.

You should use Task.Run, but not within any code you want to be reusable (i.e., library code). So you use Task.Run to call the method, not as part of the implementation of the method.

So purely CPU-bound work would look like this:

// Documentation: This method is CPU-bound.
void DoWork();

Which you would call using Task.Run:

await Task.Run(() => DoWork());

Methods that are a mixture of CPU-bound and I/O-bound should have an Async signature with documentation pointing out their CPU-bound nature:

// Documentation: This method is CPU-bound.
Task DoWorkAsync();

Which you would also call using Task.Run (since it is partially CPU-bound):

await Task.Run(() => DoWorkAsync());

await Task.Run vs await

Task.Run may post the operation to be processed at a different thread. That's the only difference.

This may be of use - for example, if LongProcess isn't truly asynchronous, it will make the caller return faster. But for a truly asynchronous method, there's no point in using Task.Run, and it may result in unnecessary waste.

Be careful, though, because the behaviour of Task.Run will change based on overload resolution. In your example, the Func<Task> overload will be chosen, which will (correctly) wait for LongProcess to finish. However, if a non-task-returning delegate was used, Task.Run will only wait for execution up to the first await (note that this is how TaskFactory.StartNew will always behave, so don't use that).

Xamarin Using async./wait VS await Task.Run (in Xamarin)

Does "async/await" always run on a separate thread from the UI?

No.

From my research, the recommendation is to use "await Task.Run" for CPU intensive tasks and "await/async" for data transfer scenarios (I/O)

That's a good general guideline for UI applications. Plain async/await doesn't use additional threads; it just keeps your UI responsive. If you have CPU-bound code, then you do need another thread, so you use Task.Run, and you can consume it using await which keeps your UI responsive while a background thread runs the CPU-bound code.

but others have recommended using await Task.Run (if needing the return) or just Task.Run (fire and forget) for ANY longer running activities in Xamarin.

I don't recommend fire-and-forget. Among other issues, it can hide exceptions. I recommend always using await.

So what is the best use for "await Task.Run" especially in the context of Xamarin, in contrast with just async/await?

The general rule above is valid: use async/await for I/O operations, and Task.Run for CPU-bound operations. Every once in a while there's an exception to that rule. E.g., sometimes I/O-bound operations are blocking and they don't provide a fully asynchronous API; in that case, it would be proper use await Task.Run to block a background thread instead of the UI thread even though the operation is technically I/O-bound and not CPU-bound.

Further reading:

  • async best practices
  • Task.Run etiquette
  • Asynchronous MVVM data binding

Using await Task.Run(() = someMethodAsync()) vs await someMethodAsync() in a UI based app

By default, when you await an incomplete operation, the sync-context is captured (if present). Then, when reactivated by the completion of the async work, that captured sync-context is used to marshal the work. Many scenarios don't have a sync-context, but UI applications such as winforms and WPF do, and the sync-context represents the UI thread (basically: this is so that the default behaviour becomes "my code works" rather than "I get cross-thread exceptions when talking to the UI in my async method"). Essentially, this means that when you await Task.Delay(1000) from a UI thread, it pauses for 1000ms (releasing the UI thread) but then resumes on the UI thread. This means that you "Tons of work to do in here" happens on the UI thread, blocking it.

Usually, the fix for this is simple: add .ConfigureAwait(false), which disables sync-context capture:

await Task.Delay(1000).ConfigureAwait(false);

(usually: add this after all await in that method)

Note that you should only do this when you know that you don't need what the sync-context offers. In particular: that you aren't going to try to touch the UI afterwards. If you do need to talk to the UI, you'll have to use the UI dispatcher explicitly.

When you used Task.Run(...), you escaped the sync-context via a different route - i.e. by not starting on the UI thread (the sync-context is specified via the active thread).

asyn await vs await task.run

The difference is in the context in which the code will be run. return "Good Job"; will be executed instantly in the separate task (and possibly on the separate thread - this is determined by the default task scheduler, like ThreadPool). In the second one, all code in GetMessage before the first await will be executed synchronously, in the same context as the caller. The rest of the code after await will be delegated to the separate task.

Task.Run() difference for awaitable Task

Lambda expressions create private methods under the hood. So this:

Task.Run(() => DoSomething());

becomes something like this:

Task.Run(__lambda);
private Task __lambda() => DoSomething();

So, asking what the difference is between these two lines:

Task.Run(() => DoSomething());
Task.Run(async() => await DoSomething());

is the same as asking what the difference is between these two methods:

private Task __lambda() => DoSomething();
private async Task __lambda() => await DoSomething();

I have a blog post that goes into detail. The summary answer is that if all you are doing is just calling DoSomething, then you can elide (remove) the async and await keywords. If the lambda is doing anything else (e.g., modifying arguments) that was removed from your question for simplicity, then I'd recommend keeping the async and await keywords.

As a general guideline: any trivial code can elide async/await; any logic should use async and await.

_ = Task.Run vs async void | Task.Run vs Async Sub

Firstly: do not use async void. I realize that it expresses the semantics of what you want, but there are some framework internals that actively explode if they encounter it (it is a long, uninteresting story), so: don't get into that practice.

Let's pretend that we have:

private async Task DoSomething() {...}

in both cases, for that reason.


The main difference here is that from the caller's perspective there is no guarantee that DoSomething won't run synchronously. So in the case:

public async task MainThread() {
_ = DoSomething(); // note use of discard here, because we're not awaiting it
}

DoSomething will run on the main thread at least as far as the first await - specifically, the first incomplete await. The good news is: you can just add:

await Task.Yield();

as the first line in DoSomething() and it is guaranteed to return immediately to the caller (because Task.Yield is always incomplete, essentially), avoiding having to go via Task.Run. Internally, Task.Yield() does something very similar to Task.Run(), but it can skip a few unnecessary pieces.

Putting that all together - if it was me, I would have:

public async Task MainThread() {
_ = DoSomething();

// Continue with other stuff and don't care about DoSomething()
}
private async Task DoSomething() {
await Task.Yield();

// Doing long running stuff
}

Understanding async / await and Task.Run()

It's as simple as you not awaiting the Task.Run, so the exception gets eaten and not returned to the call site of Task.Run.

Add "await" in front of the Task.Run, and you'll get the exception.

This will not crash your application:

private void button1_Click(object sender, EventArgs e)
{
Task.Run(() => { throw new Exception("Hello");});
}

This however will crash your application:

private async void button1_Click(object sender, EventArgs e)
{
await Task.Run(() => { throw new Exception("Hello");});
}


Related Topics



Leave a reply



Submit