When Correctly Use Task.Run and When Just Async-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());

what's the benefit of running async code with task.run()

With the 2nd option, the work is already being offloaded to a separate thread, then the asynchronicity is already gained in the scope of the program that invoked t2.

Task.Run doesn't make something asynchronous. It does permit you to run synchronous code on a thread pool thread (and thus blocking a thread pool thread instead of some other thread), but I wouldn't say it "makes it asynchronous". The code is still blocking a thread, synchronously.

Now although the work that is being done in the 1st option is declared and implemented as async, it's essentially letting code that is already being ran asynchronously, to also run itself asynchronously. What benefits from it ? Can the the scheduler or whatever manages those task threads read into this? and then perhaps give that awaiting thread some work on another task ?

Threads don't await. Methods await. When a thread is running a method and hits an await, that method is paused, the method returns to its caller, and the thread continues executing. In the case of a delegate passed to Task.Run, the await will cause the method to return -- to the thread pool, thus returning the thread to the thread pool. When the awaited task completes, a thread from the thread pool will be used to resume executing the method; this may be the same thread or a completely different thread.

More info on how exactly await works is on my blog.

if this is the case I haven't seen it mentioned in any expert blog post, or relevant page on the internet.

I have a series on Task.Run etiquette. Essentially, Task.Run is primarily used to offload synchronous or CPU-bound work off the UI thread in GUI applications. It is also occasionally useful in server-side scenarios if you want to start something processing quickly and loop back around and grab the next thing to process. There are a few other use cases but they are rare.

These reasons can all hold for asynchronous APIs. Sometimes APIs appear asynchronous but aren't actually. Or some methods are asynchronous but they do a nontrivial amount of synchronous work first, so a Task.Run is still desirable in some cases even with asynchronous code.

Is using Task.Run a short hand for async await?

I'm not sure the title of your question totally matches the actual question you're asking in the body, so I'll go with the body:

There's at least one really big difference between your two examples. With this (Option 1):

Task.Run(fn1sync());
Task.Run(fn2sync());

you have no way of waiting for both tasks to finish. They will run in parallel (PROBABLY - but not guaranteed - in different threads). They will both begin immediately and finish when they finish. Any code after that will also execute immediately, without waiting for the tasks to finish.

With this (Option 2):

var v1 = fn1Async()
var v2 = fn2Async()
...do some work..
Task.WaitAll(v1, v2)

You're holding onto the tasks and then after "do some work", you're explicitly waiting for both to finish before continuing. Nothing after the last line will execute until both are finished.

The question becomes more interesting - and maybe this is what you were getting at? - if you re-write option 1 this way (let's call it Option 3):

var v1 = Task.Run(fn1sync());
var v2 = Task.Run(fn2sync());
...do some work...
Task.WaitAll(v1, v2);

Are Option 2 and Option 3 identical? The answer is still no.

When you call an async method/delegate/etc. without awaiting it, which is what option 2 does, whether the delegate actually finishes before the calling branch can continue depends on whether the delegate invokes any truly async code. For example you could write this method:

public Task fn1sync()
{
Console.Write("Task complete!");
return Task.CompletedTask;
}

The Console.Write line will always execute before "do some work". Why? Because if you think of the return object Task literally, then fn1sync by definition can't be returning a Task to your calling code before it writes to the console (which is a synchronous operation). Replace "Console.Write" with something really slow, or maybe even blocking, and you've lost the benefits of async.

When you use Task.Run, on the other hand, you guarantee that the control flow will return to your next line of code immediately, no matter how long the delegate takes to execute and regardless whether the delegate actually contains any async code. You thus ensure that your calling thread won't get slowed down by whatever is in the delegate. In fact it's a great idea to use Task.Run when you have to invoke synchronous code that you have any reason to believe is going to be slow - legacy I/O, databases, native code, etc.

The use cases for calling an async method without awaiting it or wrapping it in Task.Run (Option 2) are perhaps less obvious but they do exist. It's a "fire and forget" approach that's fine as long as you know the code you're calling is well behaved async code (i.e. it's not going to block your thread). For example if fn1Async in Option 2 looked like this:

public Task fn1sync()
{
return Task.Run(()=>
{
Console.Write("Task complete!");
});
}

Then Options 2 and 3 would be fully equivalent and equally valid.

Update: To answer your question, "Can you tell me why does await in main thread cause it to block waiting for result, whereas await inside a function results in control passing to main thread?"

I agree with some of the comments that you might want to find a more comprehensive primer on async/await/Task. But that said, it's all about branching and how the await keyword is interpreted when it's compiled.

When you write:

async Task MyMethodAsync()
{
Console.Write("A");
int result = await AnotherMethodAsync();
Console.Write("B");
return;
}

This basically gets converted to:

Task MyMethodAsync()
{
Console.Write("A");
Task<int> task1 = AnotherMethodAsync(); // *should* return immediately if well behaved
Task task2 = task1.ContinueWith((Task<int> t) =>
{
// t is same as task1 (unsure if they're identical instances but definitely equivalent)
int result = t.Value;
Console.Write("B"); ​
​return;
​ }); // returns immediately
return task2;
}

Everything before the first awaited statement in a method executes synchronously - on the caller's thread. But everything after the first awaited statement becomes a callback that gets executed only after the first awaited statement - task1 - is finished.

Importantly, ContinueWith returns its own Task - task2 and THAT is what is is returned to the caller of MyMethodAsync, allowing them to await it or not as they choose. The line "Console.Write("B") is guaranteed not to get executed until task1 is complete. However, when it gets executed relative to the code that follows the call to MyMethodAsync
depends entirely on whether that call is awaited.

Thus:

async Task Main()
{
await MyMethodAsync();
Console.Write("C");
}

becomes:

Task Main()
{
return MyMethodAsync().ContinueWith(t =>
{
// Doesn't get executed until MyMethodAsync is done
Console.Write("C");
});
}

and the output is thus guaranteed to be: ABC

However:

void Main()
{
Task t = MyMethodAsync(); // Returns after "A" is written

// Gets executed as soon as MyMethodAsync
// branches to `await`ed code, returning the `Task`
// May or may not be before "B"
Console.Write("C");
}

executes literally, with t getting returned in an incomplete state, after "A" but before "C". The output is thus guaranteed to start with "A" but whether "B" or "C" will come next will not be predictable without knowing what AnotherMethodAsync is doing.

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");});
}

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).

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.

When to use Task.Run, when to use async- await and when to use them in combination

When to use Task.Run, when to use async- await and when to use them in combination

I have an entire blog series on the subject, but in summary:

  • Use Task.Run to move CPU-bound (or blocking) work off of the UI thread for apps with a UI.
  • Use async/await for I/O-bound work.

There are some more rare cases where Task.Run or async can be useful, but the two points above provide guidance for most situations.

Does Task.Run means background thread will behave as a blocking thread?

Task.Run can actually run synchronously or asynchronously, but it does use thread pool threads.

i am trying to create a desktop console application in .net 4.5

Trying to download multiple large images from internet. How should i use these keywords in combination and why?

This is an I/O-bound operation in a non-UI app, so you should definitely prefer async/await.



Related Topics



Leave a reply



Submit