Web API Sync Calls Best Practice - c#

Probably this question has already been made, but I never found a definitive answer. Let's say that I have a Web API 2.0 Application hosted on IIS. I think I understand that best practice (to prevent deadlocks on client) is always use async methods from the GUI event to the HttpClient calls. And this is good and it works. But what is the best practice in case I had client application that does not have a GUI (e.g. Window Service, Console Application) but only synchronous methods from which to make the call? In this case, I use the following logic:
void MySyncMethodOnMyWindowServiceApp()
{
list = GetDataAsync().Result().ToObject<List<MyClass>>();
}
async Task<Jarray> GetDataAsync()
{
list = await Client.GetAsync(<...>).ConfigureAwait(false);
return await response.Content.ReadAsAsync<JArray>().ConfigureAwait(false);
}
But unfortunately this can still cause deadlocks on client that occur at random times on random machines.
The client app stops at this point and never returns:
list = await Client.GetAsync(<...>).ConfigureAwait(false);

If it's something that can be run in the background and isn't forced to be synchronous, try wrapping the code (that calls the async method) in a Task.Run(). I'm not sure that'll solve a "deadlock" problem (if it's something out of sync, that's another issue), but if you want to benefit from async/await, if you don't have async all the way down, I'm not sure there's a benefit unless you run it in a background thread. I had a case where adding Task.Run() in a few places (in my case, from an MVC controller which I changed to be async) and calling async methods not only improved performance slightly, but it improved reliability (not sure that it was a "deadlock" but seemed like something similar) under heavier load.
You will find that using Task.Run() is regarded by some as a bad way to do it, but I really couldn't see a better way to do it in my situation, and it really did seem to be an improvement. Perhaps this is one of those things where there's the ideal way to do it vs. the way to make it work in the imperfect situation that you're in. :-)
[Updated due to requests for code]
So, as someone else posted, you should do "async all the way down". In my case, my data wasn't async, but my UI was. So, I went async down as far as I could, then I wrapped my data calls with Task.Run in such as way that it made sense. That's the trick, I think, to figure out if it makes sense that things can run in parallel, otherwise, you're just being synchronous (if you use async and immediately resolve it, forcing it to wait for the answer). I had a number of reads that I could perform in parallel.
In the above example, I think you have to async up as far as makes sense, and then at some point, determine where you can spin off a t hread and perform the operation independent of the other code. Let's say you have an operation that saves data, but you don't really need to wait for a response -- you're saving it and you're done. The only thing you might have to watch out for is not to close the program without waiting for that thread/task to finish. Where it makes sense in your code is up to you.
Syntax is pretty easy. I took existing code, changed the controller to an async returning a Task of my class that was formerly being returned.
var myTask = Task.Run(() =>
{
//...some code that can run independently.... In my case, loading data
});
// ...other code that can run at the same time as the above....
await Task.WhenAll(myTask, otherTask);
//..or...
await myTask;
//At this point, the result is available from the task
myDataValue = myTask.Result;
See MSDN for probably better examples:
https://msdn.microsoft.com/en-us/library/hh195051(v=vs.110).aspx
[Update 2, more relevant for the original question]
Let's say that your data read is an async method.
private async Task<MyClass> Read()
You can call it, save the task, and await on it when ready:
var runTask = Read();
//... do other code that can run in parallel
await runTask;
So, for this purpose, calling async code, which is what the original poster is requesting, I don't think you need Task.Run(), although I don't think you can use "await" unless you're an async method -- you'll need an alternate syntax for Wait.
The trick is that without having some code to run in parallel, there's little point in it, so thinking about multi-threading is still the point.

Using Task<T>.Result is the equivalent of Wait which will perform a synchronous block on the thread. Having async methods on the WebApi and then having all the callers synchronously blocking them effectively makes the WebApi method synchronous. Under load you will deadlock if the number of simultaneous Waits exceeds the server/app thread pool.
So remember the rule of thumb "async all the way down". You want the long running task (getting a collection of List) to be async. If the calling method must be sync you want to make that conversion from async to sync (using either Result or Wait) as close to the "ground" as possible. Keep they long running process async and have the sync portion as short as possible. That will greatly reduce the length of time that threads are blocked.
So for example you can do something like this.
void MySyncMethodOnMyWindowServiceApp()
{
List<MyClass> myClasses = GetMyClassCollectionAsync().Result;
}
Task<List<MyClass>> GetMyListCollectionAsync()
{
var data = await GetDataAsync(); // <- long running call to remote WebApi?
return data.ToObject<List<MyClass>>();
}
The key part is the long running task remains async and not blocked because await is used.
Also don't confuse the responsiveness with scalability. Both are valid reasons for async. Yes responsiveness is a reason for using async (to avoid blocking on the UI thread). You are correct this wouldn't apply to a back end service however this isn't why async is used on a WebApi. The WebApi is also a non GUI back end process. If the only advantage of async code was responsiveness of the UI layer then WebApi would be sync code from start to finish. The other reason for using async is scalability (avoiding deadlocks) and this is the reason why WebApi calls are plumbed async. Keeping the long running processes async helps IIS make more efficient use of a limited number of threads. By default there are only 12 worker threads per core. This can be raised but that isn't a magic bullet either as threads are relatively expensive (about 1MB overhead per thread). await allows you to do more with less. More concurrent long running processes on less threads before a deadlock occurs.

The problem you are having with deadlocks must stem from something else. Your use of ConfigureAwait(false) prevents deadlocks here. Solve the bug and you are fine.
See Should we switch to use async I/O by default? to which the answer is "no". You should decide on a case by case basis and choose async when the benefits outweigh the costs. It is important to understand that async IO has a productivity cost associated with it. In non-GUI scenarios only a few targeted scenarios derive any benefit at all from async IO. The benefits can be enormous, though, but only in those cases.
Here's another helpful post: https://stackoverflow.com/a/25087273/122718

Related

Proper way to start and fire-and-forget asynchronous calls?

I have an async call (DoAsyncWork()), that I would like to start in a fire-and-forget way, i.e. I'm not interesting in its result and would like the calling thread to continue even before the async method is finished.
What is the proper way to do this? I need this in both, .NET Framework 4.6 as well as .NET Core 2, in case there are differences.
public async Task<MyResult> DoWorkAsync(){...}
public void StarterA(){
Task.Run(() => DoWorkAsync());
}
public void StarterB(){
Task.Run(async () => await DoWorkAsync());
}
Is it one of those two or something different/better?
//edit: Ideally without any extra libraries.
What is the proper way to do this?
First, you need to decide whether you really want fire-and-forget. In my experience, about 90% of people who ask for this actually don't want fire-and-forget; they want a background processing service.
Specifically, fire-and-forget means:
You don't care when the action completes.
You don't care if there are any exceptions when executing the action.
You don't care if the action completes at all.
So the real-world use cases for fire-and-forget are astoundingly small. An action like updating a server-side cache would be OK. Sending emails, generating documents, or anything business related is not OK, because you would (1) want the action to be completed, and (2) get notified if the action had an error.
The vast majority of the time, people don't want fire-and-forget at all; they want a background processing service. The proper way to build one of those is to add a reliable queue (e.g., Azure Queue / Amazon SQS, or even a database), and have an independent background process (e.g., Azure Function / Amazon Lambda / .NET Core BackgroundService / Win32 service) processing that queue. This is essentially what Hangfire provides (using a database for a queue, and running the background process in-proc in the ASP.NET process).
Is it one of those two or something different/better?
In the general case, there's a number of small behavior differences when eliding async and await. It's not something you would want to do "by default".
However, in this specific case - where the async lambda is only calling a single method - eliding async and await is fine.
It depends on what you mean by proper :)
For instance: are you interested in the exceptions being thrown in your "fire and forget" calls? If not, than this is sort of fine. Though what you might need to think about is in what environment the task lives.
For instance, if this is a asp.net application and you do this inside the lifetime of a thread instantiated due to a call to a .aspx or .svc. The Task becomes a background thread of that (foreground)thread. The foreground thread might get cleaned up by the application pool before your "fire and forget" task is completed.
So also think about in which thread your tasks live.
I think this article gives you some useful information on that:
https://www.hanselman.com/blog/HowToRunBackgroundTasksInASPNET.aspx
Also note that if you do not return a value in your Tasks, a task will not return exception info. Source for that is the ref book for microsoft exam 70-483
There is probably a free version of that online somewhere ;P https://www.amazon.com/Exam-Ref-70-483-Programming-C/dp/0735676828
Maybe useful to know is that if your have an async method being called by a non-async and you wish to know its result. You can use .GetAwaiter().GetResult().
Also I think it is important to note the difference between async and multi-threading.
Async is only useful if there are operations that use other parts of a computer that is not the CPU. So things like networking or I/O operations. Using async then tells the system to go ahead and use CPU power somewhere else instead of "blocking" that thread in the CPU for just waiting for a response.
multi-threading is the allocation of operations on different threads in a CPU (for instance, creating a task which creates a background thread of the foreground thread... foreground threads being the threads that make up your application, they are primary, background threads exist linked to foreground threads. If you close the linked foreground thread, the background thread closes as well)
This allows the CPU to work on different tasks at the same time.
Combining these two makes sure the CPU does not get blocked up on just 4 threads if it is a 4 thread CPU. But can open more while it waits for async tasks that are waiting for I/O operations.
I hope this gives your the information needed to do, what ever it is you are doing :)

c# synch and asynch calls on blob upload and wait function

I am trying to understand the difference between these lines of code. I have an object of memorystream called arContents and works fine when its size is small. I see the file upload.
blockBlob.UploadFromStreamAsync(arContents)
But when the memorystream is large, the above code runs without errors but uploads no file. However when I added the WAIT() function call, this worked.
blockBlob.UploadFromStreamAsync(arContents).Wait();
I would like to understand first of all What is wait call doing and why is it an async call. Guess want to know the difference between asynch and synch calls too.
Also, I have seen the await also.. code like this
await blockBlob.UploadFromStreamAsync(arContents)
What is the difference?
Thanks
I see you tagged multithreading on your question, so I think it is important to note that asynchronous and multi-threading are two completely different things. Read through Microsoft's article called The Task asynchronous programming model in C# for a good explanation of how they are different. The example about cooking really helps make this point clear.
When you call an asynchronous method, the task (whatever that is) will start. But you usually also want to either:
Know that it finished successfully, or
Use the returned result from the task (like, if it was retrieving data)
How and when you do that is when things get interesting. The point of asynchronous programming is to allow the thread to go and do something else while waiting for something else to happen (a network request, data from the hard drive, etc) rather than the thread just sitting idle until the task completes.
You have probably experienced some programs that completely lock up and don't let you do anything while it's doing something. That is what asynchronous programming allows you to avoid.
In your first example, you aren't waiting for the result at all. That's often called "fire and forget". You start the task, then code execution immediately moves on to the next line and you will never know if the task completed successfully.
Using .Wait() is not asynchronous at all. It will lock up the thread until the task finishes. Worse than that, when you try to force an asynchronous method to be synchronous, it can sometimes cause a deadlock that your application cannot recover from. Search for c# async wait deadlock and you'll find many examples and explanations.
Ideally, you will want to use await. This is what happens when you use it:
The method (UploadFromStreamAsync) is called.
When an asynchronous operation starts, UploadFromStreamAsync returns a Task.
The await keyword will look at the Task, and if it is not complete, it will put the rest of the current method on the "to do" list for when the Task does finish and return a new Task to the calling method.
As long as you have used async all the way up the call stack, then at that point, the thread can go and do something else it has on its "to do" list. In ASP.NET it could be working on a new request that came in. In a desktop app it could be responding to user input. That would happen on the same thread.
Whenever that task finishes, then await extracts the returned value from the Task. If the method returned just a Task, then that is similar to void (no return value). If it is Task<T>, then it will return the object of type T. Then your code resumes execution after the await line.
That all sounds complicated, but you don't really need to understand completely what it's doing, and that's by design. This feature of C# lets you use asynchronous programming in a way that looks very similar to normal synchronous programming. For example:
public async Task Upload() {
...
await blockBlob.UploadFromStreamAsync(arContents);
}
Will do exactly the same as this:
public void Upload() {
...
blockBlob.UploadFromStream(arContents);
}
They look very similar, except that using async/await will give you the benefits I talked about above and the second will not.

Using awaited [async tasks] in different levels of architecture C#

I've been struggling for about some days now on checking where to do await and where not to.
I have a Repository class which fetches the data from database.
using EntityFramework the code would be something like this:
public async Task<List<Object>> GetAsync()
{
return await context.Set<Object>().ToListAsync();
}
and the consumer:
var data = await GetAsync();
and on top level I'm awaiting this method too.
should I use await on only one of these methods?
Is it a performance penalty on using resources and creates new thread each time you do await?
I have checked the questions listed in the comments and they do not reffer to the performance issues and just say that you can do it. I wanted the best practice and the reason why to / not to do so.
I'd like to add to that.
There are some async methods where there is no need to use async/await keywords. It is important to detect this kind of misuse because adding the async modifier comes at a price.
E.G. You don't need async/await keywords in your example.
public Task<List<Object>> GetAsync()
{
return context.Set<Object>().ToListAsync();
}
And then:
var data = await GetAsync();
Will be just fine. In this case, you are returning the Task<List<Object>> and then you are awaiting that in the place you directly work with objects.
I recommend installing async await helper
Let me get the essence of your question first, confusion is related to where to use the Async in the complete chain of calls and where not and how to assess the performance impact of usage, as it may lead to creation of more threads. If the synopsis goes beyond this add details to the comments, i till try to answer them too.
Let's divide and tackle each of them one by one.
Where to use the Async in the chain of calls and where not ?
Here as you are using Entity Framework to access a database, I can safely assume you are using IO based Asynchronous processing, which is the most prominent use case for Async processing across languages and frameworks, use cases for CPU based Asynchronous processing are relatively limited (will explain them too)
Async is a Scalability feature especially for IO processing instead of performance feature, in simple words via Async processing you can ensure that a hosted server can cater to many times more calls for IO processing, since calls are not blocking and they just hand over the processing request over the network, while process thread goes back to the pool ready to serve another request, complete handing over process few milliseconds
When the processing is complete, software thread need to just receive them and pass back to the client, again few millisecond, mostly its around < 1 ms, if its a pure pass through no logic call for IO
What are Benefits
Imagine instead making Synchronous call for IO to a database, where each thread involve will just wait for result to arrive, which may go in few seconds, impact will be highly negative, in general based on thread pool size, you may server 25 - 50 request at most and they too will reply on number of cores available to process, will continuously spin wasting resources, while they are idle and waiting for the response
If you make synchronous call there's no way to serve 1000+ requests in same setup and I am extremely conservative Async can actually have huge Scalability impact, where for light weight calls it may serve millions requests with ease from a single hosted process
After the background, where to use the Async in complete chain
Everywhere, feasible from begin to end, from entry point to actual exit point making IO call, since that's the actual call relieving the pool thread, as it dispatch the call over network
Do Remember though, await at a given point doesn't allow further code to process ins same method, even if it relieve the thread, so its better that if there are multiple independent calls, they are aggregated using Task.WhenAll and the representative task is awaited, it will return when all of them finish success / error, what ever may be the state
If the Async is broken in between by using something like Task.Wait or Task.Result, it will not remain pure Async call and will block the calling thread pool thread
How can Async be further enhanced ?
In Pure library calls, where Async is initiated by the Thread pool and dispatching thread can be different from receiving one and call doesn't need to reenter same context, you shall use ConfigureAwait(false), which means it will not wait to re-enter the original context and is a performance boost
Like await it makes sense to use ConfigureAwait(false) across the chain, entry to the end. This is valid only for libraries which reply extensively on thread pools
Is there a Thread created
Best read this, Stephen Cleary - There's no thread
A genuine IO async call will use Hardware based concurrency to process, will not block
the Software Threads
Variations
CPU based Asychronous processing, where you take things in background, since current thread needs to be responsive, mostly in case of Ui like WPF
Use cases
All kinds of systems especially non MS frameworks, like node js, have Async processing as underlying principle and database server cluster on receiving end is tuned to receive millions of calls and process them
B2C calls, its expected that each request is light weight with limited Payload
Edit 1:
Just in this specific case as listed here, ToListAsyncis by default Asynchronous, so you can skip async await in that case as listed in variopus comments, though do review Stepehen Cleay's article in general that may not be a very good strategy, since gains are minimal and negative impact for incorrect usage can be high

Not much difference between ASP.NET Core sync and async controller actions

I wrote a couple of action methods in a controller to test the difference between sync and async controller actions in ASP.NET core:
[Route("api/syncvasync")]
public class SyncVAsyncController : Controller
{
[HttpGet("sync")]
public IActionResult SyncGet()
{
Task.Delay(200).Wait();
return Ok(new { });
}
[HttpGet("async")]
public async Task<IActionResult> AsyncGet()
{
await Task.Delay(200);
return Ok(new { });
}
}
I then load tested the sync end point:
... followed by the async end point:
Here's the results if I increase the delay to 1000 ms
As you can see there is not much difference in the requests per second - I expected the async end point to handle more requests per second. Am I missing something?
Yes, you are missing the fact that async is not about speed, and is only slightly related to the concept of requests per second.
Async does one thing and only one thing. If a task is being awaited, and that task does not involve CPU-bound work, and as a result, the thread becomes idle, then, that thread potentially could be released to return to the pool to do other work.
That's it. Async in a nutshell. The point of async is to utilize resources more efficiently. In situations where you might have had threads tied up, just sitting there tapping their toes, waiting for some I/O operation to complete, they can instead be tasked with other work. This results in two very important ideas you should internalize:
Async != faster. In fact, async is slower. There's overhead involved in an asynchronous operation: context switching, data being shuffled on and off the heap, etc. That adds up to additional processing time. Even if we're only talking microseconds in some cases, async will always be slower than an equivalent sync process. Period. Full stop.
Async only buys you anything if your server is at load. It's only at times when your server is stressed that async will give it some much needed breathing room, whereas sync might bring it to its knees. It's all about scale. If your server is only fielding a minuscule amount of requests, you very likely will never see a difference over sync, and like I said, you may end up using more resources, ironically, because of the overhead involved.
That doesn't mean you shouldn't use async. Even if your app isn't popular today, it doesn't mean it won't be later, and rejiggering all your code at that point to support async will be a nightmare. The performance cost of async is usually negligible, and if you do end up needing it, it'll be a life-saver.
UPDATE
In the regard of keeping the performance cost of async negligible, there's a few helpful tips, that aren't obvious or really spelled out that well in most discussions of async in C#.
Use ConfigureAwait(false) as much as you possibly can.
await DoSomethingAsync().ConfigureAwait(false);
Pretty much every asynchronous method call should be followed by this except for a few specific exceptions. ConfigureAwait(false) tells the runtime that you don't need the synchronization context preserved during the async operation. By default when you await an async operation an object is created to preserve thread locals between thread switches. This takes up a large part of the processing time involved in handling an async operation, and in many cases is completely unnecessary. The only places it really matters is in things like action methods, UI threads, etc - places where there's information tied to the thread that needs to be preserved. You only need to preserve this context once, so as long as your action method, for example, awaits an async operation with the synchronization context intact, that operation itself can perform other async operations where the synchronization context is not preserved. Because of this, you should confine uses of await to a minimum in things like action methods, and instead try to group multiple async operations into a single async method that that action method can call. This will reduce the overhead involved in using async. It's worth noting that this is only a concern for actions in ASP.NET MVC. ASP.NET Core utilizes a dependency injection model instead of statics, so there are no thread locals to be concerned about. In others you can use ConfigureAwait(false) in an ASP.NET Core action, but not in ASP.NET MVC. In fact, if you try, you'll get a runtime error.
As much as possible, you should reduce the amount of locals that need to be preserved. Variables that you initialize before calling await are added to the heap and the popped back off once the task has completed. The more you've declared, the more that goes onto the heap. In particular large object graphs can wreck havoc here, because that's a ton of information to move on and off the heap. Sometimes this is unavoidable, but it's something to be mindful of.
When possible, elide the async/await keywords. Consider the following for example:
public async Task DoSomethingAsync()
{
await DoSomethingElseAsync();
}
Here, DoSomethingElseAsync returns a Task that is awaited and unwrapped. Then, a new Task is created to return from DoSometingAsync. However, if instead, you wrote the method as:
public Task DoSomethingAsync()
{
return DoSomethingElseAsync();
}
The Task returned by DoSomethingElseAsync is returned directly by DoSomethingAsync. This reduces a significant amount of overhead.
Remember that async is more about scaling than it is performance. You aren't going to see improvements in your application's ability to scale based on your performance test you have above. To properly test scaling you need to do Load Testing across in an appropriate environment that ideally matches your prod environment.
You are trying to microbenchmark performance improvements based on async alone. It is certainly possible (depending on the code/application) that you see an apparent decrease in performance. This is because there is some overhead in async code (context switching, state machines, etc.). That being said, 99% of the time, you need to write your code to scale (again, depending on your application) - rather than worry about any extra milliseconds spent here or there. In this case you are not seeing the forest for the trees so to speak. You should really be concerned with Load Testing rather than microbenchmarking when testing what async can do for you.

Is using Task.Run a bad practice?

Generally, when implementing an asynchronous method on a class, I write something like this:
public Task<Guid> GetMyObjectIdAsync(string objectName)
{
return Task.Run(() => GetMyObjectId(objectName));
}
private Guid GetMyObjectId(string objectName)
{
using (var unitOfWork = _myUnitOfWorkFactory.CreateUnitOfWork())
{
var myObject = unitOfWork.MyObjects.Single(o => o.Name == objectName);
return myObject.Id;
}
}
This sort of pattern allows me to use the same logic synchronously and asynchronously, depending on the situation (most of my work is in an old code base, not a lot supports async calls yet), as I could expose the synchronous method publicly and get maximum compatibility if I need to.
Recently I've read several SO posts that suggest using Task.Run() is a bad idea, and should only be used under certain circumstances, but those circumstances did not seem very clear.
Is the pattern I've depicted above actually a bad idea? Am I losing some of the functionality/ intended purpose of async calls doing it this way? Or is this a legit implementation?
What you are doing is offloading a synchronous operation to another thread. If your thread is "special" then that's perfectly fine. One example of a "special" thread is a UI thread. In that case you may want to offload work off of it to keep the UI responsive (another example is some kind of listener).
In most cases however you're just moving work around from one thread to another. This doesn't add any value and does add unnecessary overhead.
So:
Is the pattern I've depicted above actually a bad idea?
Yes, it is. It's a bad idea to offload synchronous work to the ThreadPool and pretend as if it's asynchronous.
Am I losing some of the functionality/ intended purpose of async calls doing it this way?
There's actually nothing asynchronous about this operation to begin with. If your executing this on a remote machine and you can benefit from doing it asynchronously the operation itself needs to be truly asynchronous, meaning:
var myObject = await unitOfWork.MyObjects.SingleAsync(o => o.Name == objectName);
What you're currently doing is called "async over sync" and you probably shouldn't do it. More in Should I expose asynchronous wrappers for synchronous methods?
Recently I've read several SO posts that suggest using Task.Run() is a bad idea, and should only be used under certain circumstances, but those circumstances did not seem very clear.
The absolutely bare bones rules of thumb I tell people who are new to asynchrony is:
First, understand the purpose. Asynchrony is for mitigating the important inefficiencies of high-latency operations.
Is the thing you're doing low-latency? Then don't make it asynchronous in any way. Just do the work. It's fast. Using a tool to mitigate latency on low-latency tasks is just making your program unnecessarily complex.
Is the thing you're doing high-latency because it is waiting on a disk to spin or a packet to show up? Make this asynchronous but do not put it on another thread. You don't hire a worker to sit by your mailbox waiting for letters to arrive; the postal system is already running asynchronously to you. You don't need to hire people to make it more asynchronous. Read "There Is No Thread" if that's not clear.
Is the high-latency work waiting on a CPU to do some enormous computation? Like a computation that is going to take well over 10 ms? Then offload that task onto a thread so that the thread can be scheduled to an idle CPU.
public Task<Guid> GetMyObjectIdAsync(string objectName)
When I see this, I expect there to be some advantage in using this method rather than just wrapping it in Task.Run() myself.
In particular, I'd expect it to release the thread when it hits some I/O or otherwise has the opportunity to do so.
Now consider if I have the code:
_resource = GetResourceForID(GetMyObjectIdAsync(SomeLongRunningWayToGetName()));
If I have a reason to need to have this done in a task, and I'm in the sort of situation where Task.Run() does actually make sense (I have a reason to offload it onto another thread) the best way to do this would be to wrap the whole thing:
Task task = Task.Run(() => _resource = GetResourceForID(GetMyObjectIdAsync(SomeLongRunningWayToGetName())));
Here Task.Run() might be a bad idea for me as the caller, or it might be good because I really am gaining from what it gives me.
However, if I see your signature I'm going to think that the best way to do this with your code would be to turn it into code that uses that method.
Task task = SomeLongRunningWayToGetName()
.ContinueWith(t => GetMyObjectIdAsync(t.Result))
.ContinueWith(t => _resource = GetResourceForIDAsync(t.Result));
(Or similar using async and await).
At best this has less good chunking of the Task.Run(). At worse I'm awaiting this just to gain from the better asynchronicity that it doesn't offer in a context that could make use of it if it was really there. (E.g I might have used this in an MVC action that I'd made asynchronous because I thought the extra overhead would be repaid in better thread-pool use).
So while Task.Run() is sometimes useful, in this case it's always bad. If you can't offer me greater asynchronicity than I can bring to the use of the class myself, don't lead me to believe you do.
Only offer a public XXXAsync() method if it really does call into asynchronous I/O.
If you really need to stub out an asynchronous method to e.g. match a signature of a shared base or interface, then it would be better as:
public Task<Guid> GetMyObjectIdAsync(string objectName)
{
return Task.FromResult(GetMyObjectId(objectName);
}
This is bad too (the caller would still have been better off just calling GetMyObjectId() directly), but at least if code awaits it then while it operates on the same thread there's no overhead of using yet another thread to do the work, so if it's mixed in with other awaits the negative impact is reduced. It's therefore useful if you really need to return a Task but can't add anything useful in how you call it.
But if you don't really need to offer it, just don't.
(A private method calling Run() because you every call site benefits from it is different, and there you're just adding convenience rather than calling Run() in several places, but that should be well-documented as such).

Categories