I'm in the process of updating a library that has an API surface that was built in .NET 3.5. As a result, all methods are synchronous. I can't change the API (i.e., convert return values to Task) because that would require that all callers change. So I'm left with how to best call async methods in a synchronous way. This is in the context of ASP.NET 4, ASP.NET Core, and .NET/.NET Core console applications.
I may not have been clear enough - the situation is that I have existing code that is not async aware, and I want to use new libraries such as System.Net.Http and the AWS SDK that support only async methods. So I need to bridge the gap, and be able to have code that can be called synchronously but then can call async methods elsewhere.
I've done a lot of reading, and there are a number of times this has been asked and answered.
Calling async method from non async method
Synchronously waiting for an async operation, and why does Wait() freeze the program here
Calling an async method from a synchronous method
How would I run an async Task<T> method synchronously?
Calling async method synchronously
How to call asynchronous method from synchronous method in C#?
The problem is that most of the answers are different! The most common approach I've seen is use .Result, but this can deadlock. I've tried all the following, and they work, but I'm not sure which is the best approach to avoid deadlocks, have good performance, and plays nicely with the runtime (in terms of honoring task schedulers, task creation options, etc). Is there a definitive answer? What is the best approach?
private static T taskSyncRunner<T>(Func<Task<T>> task)
{
T result;
// approach 1
result = Task.Run(async () => await task()).ConfigureAwait(false).GetAwaiter().GetResult();
// approach 2
result = Task.Run(task).ConfigureAwait(false).GetAwaiter().GetResult();
// approach 3
result = task().ConfigureAwait(false).GetAwaiter().GetResult();
// approach 4
result = Task.Run(task).Result;
// approach 5
result = Task.Run(task).GetAwaiter().GetResult();
// approach 6
var t = task();
t.RunSynchronously();
result = t.Result;
// approach 7
var t1 = task();
Task.WaitAll(t1);
result = t1.Result;
// approach 8?
return result;
}
So I'm left with how to best call async methods in a synchronous way.
First, this is an OK thing to do. I'm stating this because it is common on Stack Overflow to point this out as a deed of the devil as a blanket statement without regard for the concrete case.
It is not required to be async all the way for correctness. Blocking on something async to make it sync has a performance cost that might matter or might be totally irrelevant. It depends on the concrete case.
Deadlocks come from two threads trying to enter the same single-threaded synchronization context at the same time. Any technique that avoids this reliably avoids deadlocks caused by blocking.
In your code snippet, all calls to .ConfigureAwait(false) are pointless because the return value is not awaited. ConfigureAwait returns a struct that, when awaited, exhibits the behavior that you requested. If that struct is simply dropped, it does nothing.
RunSynchronously is invalid to use because not all tasks can be processed that way. This method is meant for CPU-based tasks, and it can fail to work under certain circumstances.
.GetAwaiter().GetResult() is different from Result/Wait() in that it mimics the await exception propagation behavior. You need to decide if you want that or not. (So research what that behavior is; no need to repeat it here.) If your task contains a single exception then the await error behavior is usually convenient and has little downside. If there are multiple exceptions, for example from a failed Parallel loop where multiple tasks failed, then await will drop all exceptions but the first one. That makes debugging harder.
All these approaches have similar performance. They will allocate an OS event one way or another and block on it. That's the expensive part. The other machinery is rather cheap compared to that. I don't know which approach is absolutely cheapest.
In case an exception is being thrown, that is going to be the most expensive part. On .NET 5, exceptions are processed at a rate of at most 200,000 per second on a fast CPU. Deep stacks are slower, and the task machinery tends to rethrow exceptions multiplying their cost. There are ways of blocking on a task without the exception being rethrown, for example task.ContinueWith(_ => { }, TaskContinuationOptions.ExecuteSynchronously).Wait();.
I personally like the Task.Run(() => DoSomethingAsync()).Wait(); pattern because it avoids deadlocks categorically, it is simple and it does not hide some exceptions that GetResult() might hide. But you can use GetResult() as well with this.
I'm in the process of updating a library that has an API surface that was built in .NET 3.5. As a result, all methods are synchronous. I can't change the API (i.e., convert return values to Task) because that would require that all callers change. So I'm left with how to best call async methods in a synchronous way.
There is no universal "best" way to perform the sync-over-async anti-pattern. Only a variety of hacks that each have their own drawbacks.
What I recommend is that you keep the old synchronous APIs and then introduce asynchronous APIs alongside them. You can do this using the "boolean argument hack" as described in my MSDN article on Brownfield Async.
First, a brief explanation of the problems with each approach in your example:
ConfigureAwait only makes sense when there is an await; otherwise, it does nothing.
Result will wrap exceptions in an AggregateException; if you must block, use GetAwaiter().GetResult() instead.
Task.Run will execute its code on a thread pool thread (obviously). This is fine only if the code can run on a thread pool thread.
RunSynchronously is an advanced API used in extremely rare situations when doing dynamic task-based parallelism. You're not in that scenario at all.
Task.WaitAll with a single task is the same as just Wait().
async () => await x is just a less-efficient way of saying () => x.
Blocking on a task started from the current thread can cause deadlocks.
Here's the breakdown:
// Problems (1), (3), (6)
result = Task.Run(async () => await task()).ConfigureAwait(false).GetAwaiter().GetResult();
// Problems (1), (3)
result = Task.Run(task).ConfigureAwait(false).GetAwaiter().GetResult();
// Problems (1), (7)
result = task().ConfigureAwait(false).GetAwaiter().GetResult();
// Problems (2), (3)
result = Task.Run(task).Result;
// Problems (3)
result = Task.Run(task).GetAwaiter().GetResult();
// Problems (2), (4)
var t = task();
t.RunSynchronously();
result = t.Result;
// Problems (2), (5)
var t1 = task();
Task.WaitAll(t1);
result = t1.Result;
Instead of any of these approaches, since you have existing, working synchronous code, you should use it alongside the newer naturally-asynchronous code. For example, if your existing code used WebClient:
public string Get()
{
using (var client = new WebClient())
return client.DownloadString(...);
}
and you want to add an async API, then I would do it like this:
private async Task<string> GetCoreAsync(bool sync)
{
using (var client = new WebClient())
{
return sync ?
client.DownloadString(...) :
await client.DownloadStringTaskAsync(...);
}
}
public string Get() => GetCoreAsync(sync: true).GetAwaiter().GetResult();
public Task<string> GetAsync() => GetCoreAsync(sync: false);
or, if you must use HttpClient for some reason:
private string GetCoreSync()
{
using (var client = new WebClient())
return client.DownloadString(...);
}
private static HttpClient HttpClient { get; } = ...;
private async Task<string> GetCoreAsync(bool sync)
{
return sync ?
GetCoreSync() :
await HttpClient.GetString(...);
}
public string Get() => GetCoreAsync(sync: true).GetAwaiter().GetResult();
public Task<string> GetAsync() => GetCoreAsync(sync: false);
With this approach, your logic would go into the Core methods, which may be run synchronously or asynchronously (as determined by the sync parameter). If sync is true, then the core methods must return an already-completed task. For implemenation, use synchronous APIs to run synchronously, and use asynchronous APIs to run asynchronously.
Eventually, I recommend deprecating the synchronous APIs.
I just went thru this very thing with the AWS S3 SDK. Used to be sync, and I built a bunch of code on that, but now it's async. And that's fine: they changed it, nothing to be gained by moaning about it, move on.So I need to update my app, and my options are to either refactor a large part of my app to be async, or to "hack" the S3 async API to behave like sync.I'll eventually get around to the larger async refactoring - there are many benefits - but for today I have bigger fish to fry so I chose to fake the sync.Original sync code was ListObjectsResponse response = api.ListObjects(request); and a really simple async equivalent that works for me is Task<ListObjectsV2Response> task = api.ListObjectsV2Async(rq2);ListObjectsV2Response rsp2 = task.GetAwaiter().GetResult();
While I get it that purists might pillory me for this, the reality is that this is just one of many pressing issues and I have finite time so I need to make tradeoffs. Perfect? No. Works? Yes.
You Can Call Async Method From non-async method .Check below Code .
public ActionResult Test()
{
TestClass result = Task.Run(async () => await GetNumbers()).GetAwaiter().GetResult();
return PartialView(result);
}
public async Task<TestClass> GetNumbers()
{
TestClass obj = new TestClass();
HttpResponseMessage response = await APICallHelper.GetData(Functions.API_Call_Url.GetCommonNumbers);
if (response.IsSuccessStatusCode)
{
var result = response.Content.ReadAsStringAsync().Result;
obj = JsonConvert.DeserializeObject<TestClass>(result);
}
return obj;
}
Related
I'm a bit confused on best practices with c# and tasks.
When I was taught c# and tasks it was always an async function that awaits a task. But recently I have been doing some research and found some people who say otherwise. They also say that limiting usage of async and await can improve performance and Ram usage.
For example in This Stackoverflow post It says you shouldn't always await every task but the example isnt for an ASP.NET API and there isn't any previous data used to get the new data.
This post also is in favor of not always awaiting every task. But again the example is a simple task being passed through.
So my question is for cases when there is another task on which the second task must wait. Can you use .Result or is it better us use async/await. Because I heard .Result is blocking the current thread.
But I can't see how the current thread would not be blocked by the await since the output of the first statement is needed for the second statement.
Example without async/await
public Task<User> SaveUser(User user)
{
var dbUser = _userRepository.GetByUid(user.Id).Result;
if(dbUser == null) {
dbUser = new User();
dbUser.Id = Guid.NewGuid();
}
dbUser.Name = user.Name;
return _userRepository.Save(dbUser);
}
Example with async/await
public async Task<User> SaveUser(User user)
{
var dbUser = await _userRepository.GetByUid(user.Id);
if(dbUser == null) {
dbUser = new User();
dbUser.Id = Guid.NewGuid();
}
dbUser.Name = user.Name;
return await _userRepository.Save(dbUser);
}
Note: I also heard that when using an UI, it's important that UI related tasks are awaited but this would be for API's.
I recommend reading my async intro and following up with my post on eliding async and await. TL;DR: use async/await by default, and only elide them when the method is a simple passthrough method. Since your example is not a simple passthrough method, you should keep the async/await keywords.
It's also important to draw a distinction between "asynchronous" and async. There are several kinds of asynchronous code, whereas async is an implementation detail - one specific kind of asynchronous code. Both of the methods in your question are asynchronous, but only one uses async.
They also say that limiting usage of async and await can improve performance and Ram usage.
Yes, by removing the state machine overhead of the async keyword. However, this also removes all the benefits of the state machine, so you should only remove async on simple passthrough methods.
Can you use .Result or is it better us use async/await. Because I heard .Result is blocking the current thread.
You should use async/await whenever possible, and avoid Result, even on ASP.NET Core.
But I can't see how the current thread would not be blocked by the await since the output of the first statement is needed for the second statement.
The thread is not blocked. await works by sticking a bookmark in the method and then returning a result. So the thread is not blocked, but you can think of the method as "paused". https://blog.stephencleary.com/2012/02/async-and-await.html
In a C# console app, I have a repo class with a couple of async methods:
public class SomeRepo
{
internal Task<IList<Foo>> GetAllFooAsync()
{
// this is actually fake-async due to legacy code.
var result = SomeSyncMethod();
return Task.FromResult(result);
}
public Task<IList<Foo>> GetFilteredFooAsync()
{
var allFoos = await GetAllFooAsync().ConfigureAwait(false);
return allFoos.Where(x => x.IsFiltered);
}
}
In Program.cs:
var someRepo = new SomeRepo();
var filteredFoos = someRepo.GetFilteredFooAsync(); // no await
// a couple of additional async calls (to other classes) without await..
// .. followed by:
await Task.WhenAll(filteredFoos, otherTask, anotherTask).ConfigureAwait(false);
What is baffling me is that if I place a break point on the 2nd line in Program.cs, the call to someRepo.GetFilteredFooAsync() does not proceed to the next line, but instead is stuck until the operation is complete (as though it was synchronous). Whereas if I change the call to GetAllFooAsync (in GetFilteredFooAsync) to be wrapped within a Task.Run:
public class SomeRepo
{
internal Task<IList<Foo>> GetAllFooAsync() { // ... }
public Task<IList<Foo>> GetFilteredFooAsync()
{
var allFoos = await Task.Run(() => GetAllFooAsync).ConfigureAwait(false);
return allFoos.Where(x => x.IsFiltered);
}
}
..the operation works as expected this way. Is it because GetAllFooAsync is actually synchronous, but imitating an asynchronous workflow?
EDIT: Reworded the title and added the internals of GetAllFooAsync as I've realized they could be the culprit of the issue.
You've already got some good answers that describe how async doesn't make things asynchronous, how async methods begin executing synchronously, and how await can act synchronously if its task is already completed.
So, let's talk about the design.
internal Task<IList<Foo>> GetAllFooAsync()
{
// this is actually fake-async due to legacy code.
var result = SomeSyncMethod();
return Task.FromResult(result);
}
As noted, this is a synchronous method, but it has an asynchronous signature. This is confusing; if the method is not asynchronous, it would be better with a synchronous API:
internal IList<Foo> GetAllFoo()
{
// this is actually fake-async due to legacy code.
var result = SomeSyncMethod();
return result;
}
and similar for the method that calls it:
public IList<Foo> GetFilteredFoo()
{
var allFoos = GetAllFoo();
return allFoos.Where(x => x.IsFiltered);
}
So now we have synchronous implementations with synchronous APIs. The question now is about consumption. If you are consuming this from ASP.NET, I recommend consuming them synchronously. However, if you are consuming them from a GUI application, then you can use Task.Run to offload the synchronous work to a thread pool thread, and then treat it as though it were asynchronous:
var someRepo = new SomeRepo();
var filteredFoos = Task.Run(() => someRepo.GetFilteredFoo()); // no await
// a couple of additional async calls (to other classes) without await..
// .. followed by:
await Task.WhenAll(filteredFoos, otherTask, anotherTask).ConfigureAwait(false);
Specifically, I do not recommend using Task.Run to implement, e.g., GetAllFooAsync. You should use Task.Run to call methods, not to implement them, and Task.Run should mostly be used to call synchronous code from a GUI thread (i.e., not on ASP.NET).
Presence of asynckeyword doesn't make a method asynchronous it just signals compiler to make conversion the code of the method into state machine class which is ready to be used with asynchronous flows. Effectively a method becomes asynchronous when it does an asynchronous operation such as I/O, offloading work to another thread, etc., and in that case it consists of 3 parts: the synchronous part which precedes an asynchronous operation, call of asynchronous operation which initiates the operation and returns control to calling thread, and a continuation. In your case the last two parts are absent so your call is synchronous.
Is it because GetAllFooAsync is actually synchronous, but imitating an asynchronous workflow?
Yes, Task.FromResult returns a task that is immediately RanToCompletion so it is synchronous. People often forget that Tasks may in some cases already be complete when they are returned and therefore do not run asynchronously.
This method creates a Task object whose Task.Result property is result and whose Status property is RanToCompletion. The method is commonly used when the return value of a task is immediately known without executing a longer code path. The example provides an illustration.
I'm calling an async library method with .ConfigureAwait(false). But, I still end up with deadlock. (I'm using it in ASP.NET controller API)
But, if I use the same method wrapped into Task.Run() it works fine.
My understanding is, if the libraries method is not using ConfigureAwait internally then adding ConfigureAwait won't solve the problem as in the library call it will result in deadlock (we block on it by using .Result). But, if that's the case why does it work in Task.Run() as it will fail to continue in same context/thread.
This article talks about it. Btw, I have read plenty of articles by Stephen Cleary. But, why Task.Run() works is a mystery.
Code snippet:
// This Create Method results in Deadlock
public async Task<string> Create(MyConfig config)
{
Document doc = await Client.CreateDocumentAsync(CollectionUri, config).ConfigureAwait(false);
return doc.Id;
}
// Uses Task.Run() which works properly, why??
public string Create(MyConfig config)
{
Document doc = Task.Run(() => Client.CreateDocumentAsync(CollectionUri, config)).Result;
return doc.Id;
}
[HttpPost]
public ActionResult CreateConfig(MyConfig config)
{
string id = Create(config).Result;
return Json(id);
}
I believe Lukazoid is correct. To put it another way...
// This Create Method results in Deadlock
public async Task<string> Create(MyConfig config)
{
Document doc = await Client.CreateDocumentAsync(CollectionUri, config).ConfigureAwait(false);
return doc.Id;
}
You can't just stick a ConfigureAwait(false) at one level and have it magically prevent deadlocks. ConfigureAwait(false) can only prevent deadlocks if it is used by every await in the transitive closure of that method and all methods it calls.
In other words, ConfigureAwait(false) needs to be used for every await in Create (which it is), and it also needs to be used for every await in CreateDocumentAsync (which we don't know), and it also needs to be used for every await in every method that CreateDocumentAsync calls, etc.
This is one reason why it is such a brittle "solution" to the deadlock problem.
In the first example, the implementation of Client.CreateDocumentAsync is deadlocking because it is trying to execute a continuation using the current SynchronizationContext.
When using Task.Run, the delegate will be invoked on a ThreadPool thread, this means there will be no current SynchronizationContext so all continuations will be resumed using a ThreadPool thread. This means it will not deadlock.
Out of interest, why is your CreateConfig method not async? Most recent versions of MVC and WebAPI support asynchronous methods, getting rid of the .Result would be the best solution.
Just an observation: i also noticed that doing this will also result in a deadlock
private string Create(Task<Document> task)
{
var doc = Task.Run(() => task).Result;
return doc.Id;
}
[HttpPost]
public ActionResult CreateConfig(MyConfig config)
{
var task = Client.CreateDocumentAsync(CollectionUri, config);
var id = Create(task).Result;
return Json(id);
}
So even running things on the thread pool may not be the ultimate solution. It seems an equally import factor to consider is what SynchonizationContext was in effect when the async method's task was created.
I am trying to understand concurrency by doing it in code. I have a code snippet which I thought was running asynchronously. But when I put the debug writeline statements in, I found that it is running synchronously. Can someone explain what I need to do differently to push ComputeBB() onto another thread using Task.Something?
Clarification I want this code to run ComputeBB in some other thread so that the main thread will keep on running without blocking.
Here is the code:
{
// part of the calling method
Debug.WriteLine("About to call ComputeBB");
returnDTM.myBoundingBox = await Task.Run(() => returnDTM.ComputeBB());
Debug.WriteLine("Just called await ComputBB.");
return returnDTM;
}
private ptsBoundingBox2d ComputeBB()
{
Debug.WriteLine("Starting ComputeBB.");
Stopwatch sw = new Stopwatch(); sw.Start();
var point1 = this.allPoints.FirstOrDefault().Value;
var returnBB = new ptsBoundingBox2d(
point1.x, point1.y, point1.z, point1.x, point1.y, point1.z);
Parallel.ForEach(this.allPoints,
p => returnBB.expandByPoint(p.Value.x, p.Value.y, p.Value.z)
);
sw.Stop();
Debug.WriteLine(String.Format("Compute BB took {0}", sw.Elapsed));
return returnBB;
}
Here is the output in the immediate window:
About to call ComputeBB
Starting ComputeBB.
Compute BB took 00:00:00.1790574
Just called await ComputBB.
Clarification If it were really running asynchronously it would be in this order:
About to call ComputeBB
Just called await ComputBB.
Starting ComputeBB.
Compute BB took 00:00:00.1790574
But it is not.
Elaboration
The calling code has signature like so: private static async Task loadAsBinaryAsync(string fileName) At the next level up, though, I attempt to stop using async. So here is the call stack from top to bottom:
static void Main(string[] args)
{
aTinFile = ptsDTM.CreateFromExistingFile("TestSave.ptsTin");
// more stuff
}
public static ptsDTM CreateFromExistingFile(string fileName)
{
ptsDTM returnTin = new ptsDTM();
Task<ptsDTM> tsk = Task.Run(() => loadAsBinaryAsync(fileName));
returnTin = tsk.Result; // I suspect the problem is here.
return retunTin;
}
private static async Task<ptsDTM> loadAsBinaryAsync(string fileName)
{
// do a lot of processing
Debug.WriteLine("About to call ComputeBB");
returnDTM.myBoundingBox = await Task.Run(() => returnDTM.ComputeBB());
Debug.WriteLine("Just called await ComputBB.");
return returnDTM;
}
I have a code snippet which I thought was running asynchronously. But when I put the debug writeline statements in, I found that it is running synchronously.
await is used to asynchronously wait an operations completion. While doing so, it yields control back to the calling method until it's completion.
what I need to do differently to push ComputeBB() onto another thread
It is already ran on a thread pool thread. If you don't want to asynchronously wait on it in a "fire and forget" fashion, don't await the expression. Note this will have an effect on exception handling. Any exception which occurs inside the provided delegate would be captured inside the given Task, if you don't await, there is a chance they will go about unhandled.
Edit:
Lets look at this piece of code:
public static ptsDTM CreateFromExistingFile(string fileName)
{
ptsDTM returnTin = new ptsDTM();
Task<ptsDTM> tsk = Task.Run(() => loadAsBinaryAsync(fileName));
returnTin = tsk.Result; // I suspect the problem is here.
return retunTin;
}
What you're currently doing is synchronously blocking when you use tsk.Result. Also, for some reason you're calling Task.Run twice, once in each method. That is unnecessary. If you want to return your ptsDTM instance from CreateFromExistingFile, you will have to await it, there is no getting around that. "Fire and Forget" execution doesn't care about the result, at all. It simply wants to start whichever operation it needs, if it fails or succeeds is usually a non-concern. That is clearly not the case here.
You'll need to do something like this:
private PtsDtm LoadAsBinary(string fileName)
{
Debug.WriteLine("About to call ComputeBB");
returnDTM.myBoundingBox = returnDTM.ComputeBB();
Debug.WriteLine("Just called ComputeBB.");
return returnDTM;
}
And then somewhere up higher up the call stack, you don't actually need CreateFromExistingFiles, simply call:
Task.Run(() => LoadAsBinary(fileName));
When needed.
Also, please, read the C# naming conventions, which you're currently not following.
await's whole purpose is in adding the synchronicity back in asynchronous code. This allows you to easily partition the parts that are happenning synchronously and asynchronously. Your example is absurd in that it never takes any advantage whatsoever of this - if you just called the method directly instead of wrapping it in Task.Run and awaiting that, you would have had the exact same result (with less overhead).
Consider this, though:
await
Task.WhenAll
(
loadAsBinaryAsync(fileName1),
loadAsBinaryAsync(fileName2),
loadAsBinaryAsync(fileName3)
);
Again, you have the synchronicity back (await functions as the synchronization barrier), but you've actually performed three independent operations asynchronously with respect to each other.
Now, there's no reason to do something like this in your code, since you're using Parallel.ForEach at the bottom level - you're already using the CPU to the max (with unnecessary overhead, but let's ignore that for now).
So the basic usage of await is actually to handle asynchronous I/O rather than CPU work - apart from simplifying code that relies on some parts of CPU work being synchronised and some not (e.g. you have four threads of execution that simultaneously process different parts of the problem, but at some point have to be reunited to make sense of the individual parts - look at the Barrier class, for example). This includes stuff like "making sure the UI doesn't block while some CPU intensive operation happens in the background" - this makes the CPU work asynchronous with respect to the UI. But at some point, you still want to reintroduce the synchronicity, to make sure you can display the results of the work on the UI.
Consider this winforms code snippet:
async void btnDoStuff_Click(object sender, EventArgs e)
{
lblProgress.Text = "Calculating...";
var result = await DoTheUltraHardStuff();
lblProgress.Text = "Done! The result is " + result;
}
(note that the method is async void, not async Task nor async Task<T>)
What happens is that (on the GUI thread) the label is first assigned the text Calculating..., then the asynchronous DoTheUltraHardStuff method is scheduled, and then, the method returns. Immediately. This allows the GUI thread to do whatever it needs to do. However - as soon as the asynchronous task is complete and the GUI is free to handle the callback, the execution of btnDoStuff_Click will continue with the result already given (or an exception thrown, of course), back on the GUI thread, allowing you to set the label to the new text including the result of the asynchronous operation.
Asynchronicity is not an absolute property - stuff is asynchronous to some other stuff, and synchronous to some other stuff. It only makes sense with respect to some other stuff.
Hopefully, now you can go back to your original code and understand the part you've misunderstood before. The solutions are multiple, of course, but they depend a lot on how and why you're trying to do what you're trying to do. I suspect you don't actually need to use Task.Run or await at all - the Parallel.ForEach already tries to distribute the CPU work over multiple CPU cores, and the only thing you could do is to make sure other code doesn't have to wait for that work to finish - which would make a lot of sense in a GUI application, but I don't see how it would be useful in a console application with the singular purpose of calculating that single thing.
So yes, you can actually use await for fire-and-forget code - but only as part of code that doesn't prevent the code you want to continue from executing. For example, you could have code like this:
Task<string> result = SomeHardWorkAsync();
Debug.WriteLine("After calling SomeHardWorkAsync");
DoSomeOtherWorkInTheMeantime();
Debug.WriteLine("Done other work.");
Debug.WriteLine("Got result: " + (await result));
This allows SomeHardWorkAsync to execute asynchronously with respect to DoSomeOtherWorkInTheMeantime but not with respect to await result. And of course, you can use awaits in SomeHardWorkAsync without trashing the asynchronicity between SomeHardWorkAsync and DoSomeOtherWorkInTheMeantime.
The GUI example I've shown way above just takes advantage of handling the continuation as something that happens after the task completes, while ignoring the Task created in the async method (there really isn't much of a difference between using async void and async Task when you ignore the result). So for example, to fire-and-forget your method, you could use code like this:
async void Fire(string filename)
{
var result = await ProcessFileAsync(filename);
DoStuffWithResult(result);
}
Fire("MyFile");
This will cause DoStuffWithResult to execute as soon as result is ready, while the method Fire itself will return immediately after executing ProcessFileAsync (up to the first await or any explicit return someTask).
This pattern is usually frowned upon - there really isn't any reason to return void out of an async method (apart from event handlers); you could just as easily return Task (or even Task<T> depending on the scenario), and let the caller decide whether he wants his code to execute synchronously in respect to yours or not.
Again,
async Task FireAsync(string filename)
{
var result = await ProcessFileAsync(filename);
DoStuffWithResult(result);
}
Fire("MyFile");
does the same thing as using async void, except that the caller can decide what to do with the asynchronous task. Perhaps he wants to launch two of those in parallel and continue after all are done? He can just await Task.WhenAll(Fire("1"), Fire("2")). Or he just wants that stuff to happen completely asynchronously with respect to his code, so he'll just call Fire("1") and ignore the resulting Task (of course, ideally, you at the very least want to handle possible exceptions).
If I wanted to write a non-blocking web api action by returning a Task object, I could do it with or without using the async keyword as such:
Using async
public async Task<HttpResponseMessage> Get()
{
Func<HttpResponseMessage> slowCall = () =>
{
Thread.Sleep(2000);
return Request.CreateResponse(HttpStatusCode.OK, "Hello world");
};
var task = Task<HttpResponseMessage>.Factory.StartNew(slowCall);
return await task;
}
Without using async
public Task<HttpResponseMessage> Get()
{
Func<HttpResponseMessage> slowCall = () =>
{
Thread.Sleep(2000);
return Request.CreateResponse(HttpStatusCode.OK, "Hello world");
};
var task = Task<HttpResponseMessage>.Factory.StartNew(slowCall);
return task;
}
They both work properly. However, every example I've seen (online and in books) on writing a web api action that returns a Task uses the async keyword. Granted, I understand that gives you more flexibility as it allows you to control what you want to "await" on and what not. But assuming your functionality could be effectively handled either way,
Is there any advantage to using one approach vs the other?
Should I always use the async keyword (and if so why)?
Or is it irrelevant?
The async keyword allows you to use an await in your method by creating a state machine. If you can manage returning an asynchronous task without using it you can go ahead and remove it because it has some (very small) overhead. Keep in mind though that it's only useful in a few cases. Your return await is one of them.
Another difference is how exceptions are handled. If there's an exception in the synchronous part of the method and it's marked as async the exception will be stored in the returned task. Without the keyword the exception would be thrown regularly. For example in this case there's a big difference:
var task = Get(); // unhandled exception without async
try
{
var result = await task; // handled exception with async
}
catch
{
}
My recommendation is to use the async keyword even when you don't absolutely need to*, because most developers don't understand the difference and the value in the optimization is mostly negligible.
* Unless you and your teammates really know what you're doing.
There is one advantage to bypassing the await and returning the Task directly: performance. You won't allocate or process the state machine that goes with an async method. There are, however, some subtle differences when exceptions are involved.
In the async example, any exceptions thrown will be enclosed in a Task. This is generally what people assume will happen when they call a Task-returning method. In the sync example, exceptions will be thrown immediately upon invocation.
This will also have an effect on the exception's stack trace. In the async example, it will show Get(). In the sync example, it will show your anonymous delegate or worse, some internal crap in Task.Factory.StartNew with no actual reference to your actual code. This can result in being a little more difficult to debug.