Do You Have to Put Task.Run in a Method to Make It Async

Do you have to put Task.Run in a method to make it async?

First, let's clear up some terminology: "asynchronous" (async) means that it may yield control back to the calling thread before it starts. In an async method, those "yield" points are await expressions.

This is very different than the term "asynchronous", as (mis)used by the MSDN documentation for years to mean "executes on a background thread".

To futher confuse the issue, async is very different than "awaitable"; there are some async methods whose return types are not awaitable, and many methods returning awaitable types that are not async.

Enough about what they aren't; here's what they are:

  • The async keyword allows an asynchronous method (that is, it allows await expressions). async methods may return Task, Task<T>, or (if you must) void.
  • Any type that follows a certain pattern can be awaitable. The most common awaitable types are Task and Task<T>.

So, if we reformulate your question to "how can I run an operation on a background thread in a way that it's awaitable", the answer is to use Task.Run:

private Task<int> DoWorkAsync() // No async because the method does not need await
{
return Task.Run(() =>
{
return 1 + 2;
});
}

(But this pattern is a poor approach; see below).

But if your question is "how do I create an async method that can yield back to its caller instead of blocking", the answer is to declare the method async and use await for its "yielding" points:

private async Task<int> GetWebPageHtmlSizeAsync()
{
var client = new HttpClient();
var html = await client.GetAsync("http://www.example.com/");
return html.Length;
}

So, the basic pattern of things is to have async code depend on "awaitables" in its await expressions. These "awaitables" can be other async methods or just regular methods returning awaitables. Regular methods returning Task/Task<T> can use Task.Run to execute code on a background thread, or (more commonly) they can use TaskCompletionSource<T> or one of its shortcuts (TaskFactory.FromAsync, Task.FromResult, etc). I don't recommend wrapping an entire method in Task.Run; synchronous methods should have synchronous signatures, and it should be left up to the consumer whether it should be wrapped in a Task.Run:

private int DoWork()
{
return 1 + 2;
}

private void MoreSynchronousProcessing()
{
// Execute it directly (synchronously), since we are also a synchronous method.
var result = DoWork();
...
}

private async Task DoVariousThingsFromTheUIThreadAsync()
{
// I have a bunch of async work to do, and I am executed on the UI thread.
var result = await Task.Run(() => DoWork());
...
}

I have an async/await intro on my blog; at the end are some good followup resources. The MSDN docs for async are unusually good, too.

Does running a synchronous method using Task.Run() make it asynchronous


Here the method I've written to create a dictionary out of my List, does it run asynchronously?

It's probably more accurate to say it runs in parallel. A separate thread is created to produce the result. If people isn't being used in a thread-safe way (e.g. it's a List that you're adding to in other threads), then you could end up with messy race conditions.

When people talk about "Asynchronous" code, there are two things they could mean.

  1. Code that models an inherently asynchronous operation, like Disk or Network I/O, in a way that doesn't block a thread.
  2. More broadly, any kind of operation that can complete independently from the current thread of operations.

What you've done matches the second definition, but not the first. If people were backed by a database round-trip, your code would still block a thread while that call completed: it would just be blocking a different thread than the one that calls MapDepartments.

What's the difference between this and putting my dictionary logic in an asynchronous method?

Since you're pulling from an in-memory List, and all your operations are CPU-bound (not doing truly asynchronous operations), there would be no benefit to putting your logic in an async method, because there would be nothing for that method to await. Even though the method would say it's async, it would run synchronously to completion before returning a Task.

Assuming creating that dictionary really takes enough time to make it worth multi-threading, and assuming you're not modifying the underlying list before you await the Task, your current approach is better.

Whatever I've implemented is this somewhat similar to having a .ToDictionaryAsync() method?

I mean, I guess you could write a ToDictionaryAsync method that just performs ToDictionary on a separate thread, but that's really not following the spirit of what Async means.

When a library like Entity Framework provides a ToDictionaryAsync() method, that's because they know there's going to be an actual database call that will actually be asynchronous, and they know how to make that happen in a non-blocking way without calling ToDictionary(). I would reserve ToDictionaryAsync for situations like that.

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.

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.

Using async instead of Task.Run()

You would mark your method as async, await the Task.Run so the continuations run on the UI, also leaving only the long running (seemingly CPU bound) job within it

private async void btnAction_Click(object sender, RoutedEventArgs e)
{
btnAction.IsEnabled = false;
txtResult.Text = "";
lblStatus.Text = "Wait...";

string input = query.Text;

// calling a method that takes a long time (>3s) to finish and return
var attempt = await Task.Run(() => someLibrary.doSomethingWith(input));

if (attempt.ContainsKey("success"))
{
if (attempt["success"] == true)
{
txtResult.Text = "Success! The door is: " + (attempt["is_open"] ? "open" : "closed");
lblStatus.Text = "";
}
else
{
lblStatus.Text = "Error! The service says: " + attempt["errorMessage"];
}
}
else
{
MessageBox.Show("There was a problem getting results from web service.");
lblStatus.Text = "";
}

btnAction.IsEnabled = true;

}

Update

To cancel the task, you would use a CancellationToken from am instance of CancellationTokenSource and pass that into Task.Run and also your long running method to check for IsCancellationRequested (if you can). You cancel by calling CancellationTokenSource.Cancel

Note you would likely want to wrap this in a try catch finally and catch on OperationCanceledException and place your button enabling code in the finally

Use async/await or Task.Run in a continuous work?


So, is it necessary to change Task.Run to async/await form?

No, if you don't care about any return value from the task, you don't have to to await it. You could just set IsWorking to false to let the thread finish eventually.

If you want to be able to determine when it has finished, you should keep a reference to the Task and await it.

If you want to run something on a background thread "forever" or for a very long time, you should either create a Thread or use the overload of Task.Factory.StartNew that accepts a TaskCreationOptions.LongRunning:

Task.Factory.StartNew(() => { ... }, 
TaskCreationOptions.LongRunning);

This gives the TPL a hint to run your action on a dedicated thread rather than "stealing" one from the thread pool.

How to assign Task.Run(await some async function that returns byte[]) to a variable

You could just await the result of the existing Task.Run invocation:

byte[] bytes =  await Task.Run(async () => await PrintLetter(cid));

In this case the async/await can be safely elided from the lambda. The behavior of the code below is identical:

byte[] bytes =  await Task.Run(() => PrintLetter(cid));

Removing the Task.Run is not a no-op though. It might be OK, or it might not. You can check out this question for guidance: await Task.Run vs await



Related Topics



Leave a reply



Submit