I have an ASP.NET Core 3.1 based project written using C#. I am aware that the best time to use await and async is when accessing external resources like pulling data from the database, accessing files, or making an HTTP request. This frees up the thread so more work is done instead for the thread to sit around waiting for code to finish.
However, I am trying to figure out at what point using async/await will hurt performance? Does the process of releasing the thread when await is called and retrieving a thread when the task complete have cost?
The code found next, is called asynchronously. In reality, that code does not need to be called asynchronously since all the code is executed in memory and no external requests are made.
public interface ILocator
{
Task<Model> ExampleAsync();
}
public class Example : Controller
{
public ILocator Locator { get; set; }
public Example(ILocator locator)
{
Locator = locator;
}
public async Task<IActionResult> Example()
{
Model model = await Locator.ExampleAsync();
return View(model);
}
}
public class Locator : ILocator
{
pubilc Task ExampleAsync()
{
Example model = new Example();
model.Status = "New";
return Task.CompletedTask;
}
}
Here is the synchronous version of the above code
public interface ILocator
{
Model Example();
}
public class Example : Controller
{
public ILocator Locator { get; set; }
public Example(ILocator locator)
{
Locator = locator;
}
public IActionResult Example()
{
Model model = Locator.Example();
return View(model);
}
}
public class Locator : ILocator
{
pubilc Example()
{
Example model = new Example();
model.Status = "New";
}
}
Will the asynchronous version of the code have higher cost/lower performance than the synchronous version due to the unnecessary await/async usage?
When will async/await do more harm than good?
You typically use async/await when performing I/O bound tasks like reading from a stream, reading from a DB, sending something over the network or waiting for a response.
This makes the thread available to do some other (CPU related work).
Technically, async/await is slower in terms of raw performance, however, it increases the scalability of your application since it allows threads to be available for other work while others are waiting for I/O bound operations.
I have an ASP.NET Core 3.1 based project written using C#.
That means that async/await will allow you to increase your capacity (in requests pers second) a great deal.
at what point using async/await will hurt performance?
it will add a little bit of overhead that is a fixed amount. It will in general be small compared to the costs of the I/O and not really noticable.
When will async/await do more harm than good?
In a web site/service that will never see a load above ~50 requests / second.
And even then the 'harm' will be very small.
Actual numbers depend on the hardware, amount of I/O work etc.
In reality, that code does not need to be called asynchronously since all the code is executed in memory
In that case it will be faster to handle it synchronously.
But I know teams that prefer to have all Actions async, just for uniformity. And since the overhead is so small I consider that a valid approach too.
Related
First of all, I couldn't make the title more explanatory, I will try to lay out the problem then provide my solution for it
I'm implementing a backend in asp core for our game, we have few requests that are somewhat large, like requesting the items we provide in the store, every user starts the game loads the store info which makes a database trip to pull the entire store info, which RARELY change -less than once a month-, so we are making thousands of database trip that aren't needed.
on top of that we return timestamps for when was the last time an item image has changed, the images are stored in a blob which makes me query the blob for change date, which makes the request way costlier
so to solve all of this, I implemented a small class to cache the request until we need to update it,for this request and some others, but I'm not sure if I'm looking at this correctly
here is the base abstract class:
public abstract class CachedModel<T>
{
protected T Model { get; set; }
private readonly SemaphoreSlim semaphore = new SemaphoreSlim(1,1);
protected abstract Task ThreadSafeUpdateAsync();
protected abstract bool NeedsUpdate();
public async Task<T> GetModel()
{
if (NeedsUpdate())
{
try
{
await semaphore.WaitAsync();
if(NeedsUpdate()) // not sure if this is needed, can other threads enter here after the first one already updated the object?
await ThreadSafeUpdateAsync();
}
finally
{
semaphore.Release();
}
}
return Model;
}
}
and then I implement this class per request like this:
public class CachedStoreInfo : CachedModel<DesiredModel>
{
protected override async Task ThreadSafeUpdateAsync()
{
// make the trip to db and Blob service
Model = some result
}
protected override bool NeedsUpdate()
{
return someLogicToDecideIfNeedsUpdate;
}
}
finally, in the asp controller all what I need to do is this:
[HttpGet]
public async Task<DesiredModel> GetStoreInfo()
{
return await cachedStoreInfo.GetModel();
}
Is this a proper implementation ? and is this even necessary or there is a smarter way to achieve this? getting the time stamps from the blob was the main reason I though about caching the result
Your implementation looks correct. Of course the instance of CachedStoreInfo should be a singleton in a required scope (as I understand in your case it should be a singleton in scope of application).
can other threads enter here after the first one already updated the object?
As Kevin Gosse noted other threads can enter here. Your second check for NeedsUpdate() is a part of Double-checked locking pattern. And it might be a good optimization.
and is this even necessary or there is a smarter way to achieve this?
As for me your implementation is minimalist and smart enough
I hope this is still on-topic. In this post here I saw how to create an await ViewAsync():
Returning a view with async keyword
So my consideration was: okay, I want to make my application use multithreading, let's make a BaseController that contains those methods for ViewAsync:
just a part of it:
public class BaseController : Controller
{
[NonAction]
public virtual async Task<ViewResult> ViewAsync()
{
return await Task.Run(() => this.View(null));
}
[NonAction]
public virtual async Task<ViewResult> ViewAsync(string viewName)
{
return await Task.Run(() => this.View(viewName, this.ViewData.Model));
}
// the other implementations....
}
now I could always call like this in the inheriting class:
[HttpGet]
public async Task<IActionResult> DoSomething()
{
// maybe we need to do something here, maybe not
return await ViewAsync(new DoSomethingObject());
}
imho, my advantage/target is performance since I always can use multithreading now.
Am I right with my consideration?
In the post, a comment to an answer started with I wouldn't do this. But there aren't many votes/comments/answers.. Where are risks or disadvantages of such an implementation? And maybe, why doesn't Microsoft.AspNetCore.Mvc.Controller come with the method ViewAsync?
Any web app already uses multithreading. When a request comes in, a thread from the thread pool handles it. Your app already handles multiple requests at the same time, without you using Task.Run.
Async/await stuff is useful so that you don't block threads from the thread pool while you wait for async operations to complete (like querying a DB).
Starting new tasks Task.Run on the default thread pool is pretty much useless, unless you are doing some CPU intensive work. Let's say you have an endpoint that calculates the prime numbers up to n on each request, in that case you could delegate a new task on the default thread pool that returns the prime numbers, and have a view that renders them to some html.
Rendering a view has nothing asynchronous about it, so a ViewAsync is not needed. Preparing the data for a view probably is async, but the rendering is not. Also, having a ViewAsync would probably over complicate the template syntax.
I want to run a method asynchronously from my web api code but I am not getting the desired behavior.
I am trying to do the LongCallMethod in an asynchronous way. So that I am able to return success to my user before the LongCallMethod completes.
I have tried various combination of calls with await and async but not getting any success still.
I might not be understanding the concept of async/await correctly.
Can you please let me know what's the mistake I am making?
My controller code is below
[HttpGet("{id}")]
public string Get(int id)
{
PracticeAsync p = new PracticeAsync();
p.LongCallMethod();
return "Success";
}
The code for the class PracticeAsync is below:
public class PracticeAsync
{
public async Task LongCallMethod()
{
var x = await CallThis();
}
public async Task<dynamic> CallThis()
{
Thread.Sleep(10000); //DB call
return "done";
}
}
I am trying to do the LongCallMethod in an asynchronous way. So that I am able to return success to my user before the LongCallMethod completes.
That's not how async works.
You'll need to use SignalR or a proper distributed architecture. Specifically, your webapp should place its work into a reliable queue, and have an independent service read from that queue and do the work. ASP.NET was designed to serve web requests, and has severe reliability limitations if you try to force it to do otherwise.
I am working with async actions and use the HttpContext.Current.User like this
public class UserService : IUserService
{
public ILocPrincipal Current
{
get { return HttpContext.Current.User as ILocPrincipal; }
}
}
public class ChannelService : IDisposable
{
// In the service layer
public ChannelService()
: this(new Entities.LocDbContext(), new UserService())
{
}
public ChannelService(Entities.LocDbContext locDbContext, IUserService userService)
{
this.LocDbContext = locDbContext;
this.UserService = userService;
}
public async Task<ViewModels.DisplayChannel> FindOrDefaultAsync(long id)
{
var currentMemberId = this.UserService.Current.Id;
// do some async EF request …
}
}
// In the controller
[Authorize]
[RoutePrefix("channel")]
public class ChannelController : BaseController
{
public ChannelController()
: this(new ChannelService()
{
}
public ChannelController(ChannelService channelService)
{
this.ChannelService = channelService;
}
// …
[HttpGet, Route("~/api/channels/{id}/messages")]
public async Task<ActionResult> GetMessages(long id)
{
var channel = await this.ChannelService
.FindOrDefaultAsync(id);
return PartialView("_Messages", channel);
}
// …
}
I have the code recently refactored, previously I had to give the user on each call to the service.
Now I read this article https://www.trycatchfail.com/2014/04/25/using-httpcontext-safely-after-async-in-asp-net-mvc-applications/ and I’m not sure if my code still works.
Has anyone a better approach to handle this? I don’t want to give the user on every request to the service.
As long as your web.config settings are correct, async/await works perfectly well with HttpContext.Current. I recommend setting httpRuntime targetFramework to 4.5 to remove all "quirks mode" behavior.
Once that is done, plain async/await will work perfectly well. You'll only run into problems if you're doing work on another thread or if your await code is incorrect.
First, the "other thread" problem; this is the second problem in the blog post you linked to. Code like this will of course not work correctly:
async Task FakeAsyncMethod()
{
await Task.Run(() =>
{
var user = _userService.Current;
...
});
}
This problem actually has nothing to do with asynchronous code; it has to do with retrieving a context variable from a (non-request) thread pool thread. The exact same problem would occur if you try to do it synchronously.
The core problem is that the asynchronous version is using fake asynchrony. This inappropriate, especially on ASP.NET. The solution is to simply remove the fake-asynchronous code and make it synchronous (or truly asynchronous, if it actually has real asynchronous work to do):
void Method()
{
var user = _userService.Current;
...
}
The technique recommended in the linked blog (wrapping the HttpContext and providing it to the worker thread) is extremely dangerous. HttpContext is designed to be accessed only from one thread at a time and AFAIK is not threadsafe at all. So sharing it among different threads is asking for a world of hurt.
If the await code is incorrect, then it causes a similar problem. ConfigureAwait(false) is a technique commonly used in library code to notify the runtime that it doesn't need to return to a specific context. Consider this code:
async Task MyMethodAsync()
{
await Task.Delay(1000).ConfigureAwait(false);
var context = HttpContext.Current;
// Note: "context" is not correct here.
// It could be null; it could be the correct context;
// it could be a context for a different request.
}
In this case, the problem is obvious. ConfigureAwait(false) is telling ASP.NET that the rest of the current method does not need the context, and then it immediately accesses that context. When you start using context values in your interface implementations, though, the problem is not as obvious:
async Task MyMethodAsync()
{
await Task.Delay(1000).ConfigureAwait(false);
var user = _userService.Current;
}
This code is just as wrong but not as obviously wrong, since the context is hidden behind an interface.
So, the general guideline is: use ConfigureAwait(false) if you know that the method does not depend on its context (directly or indirectly); otherwise, do not use ConfigureAwait. If it's acceptable in your design to have interface implementations use the context in their implementation, then any method that calls an interface method should not use ConfigureAwait(false):
async Task MyMethodAsync()
{
await Task.Delay(1000);
var user = _userService.Current; // works fine
}
As long as you follow that guideline, async/await will work perfectly with HttpContext.Current.
Async is fine. The problem is when you post the work to a different thread. If your application is setup as 4.5+, the asynchronous callback will be posted in the original context, so you'll also have the proper HttpContext etc.
You don't want to access shared state in a different thread anyway, and with Tasks, you rarely need to handle that explicitly - just make sure you put all your inputs as arguments, and only return a response, rather than reading or writing to a shared state (e.g. HttpContext, static fields etc.)
There is no problem, if your ViewModels.DisplayChannel is a simple object without additional logic.
A problem may occur, if the result of your Task references to "some context objects", f.e. to HttpContext.Current. Such objects are often attached to the thread, but entire code after await may be executed in another thread.
Keep in mind, that UseTaskFriendlySynchronizationContext doesn't solve all your problems. If we are talking about ASP.NET MVC, this setting ensures that Controller.HttpContext contains correct value as before await as after. But it doesn't ensure that HttpContext.Current contains correct value, and after await it still can be null.
I have a WPF MVVM Application. The View Model has a couple of properties bound to the view, and those properties are populated by data coming from a database directly or through a wcf service that stays in between the view model and the database. The choice of the mode of data connection depends on the app setting in the App.config file of the client application. I want to implement my own way of calling service methods asynchronously and handling their return values. I would like to know if there are chances for threading issues if I implement it the following way using Tasks:
The service call flow:
ViewModel > ServiceAgent > (MyWCFServiceClient or MyBusinessClient ) > MyBusinessClass> Database
Inorder to consume the service operations I have a MyWCFServiceClient class that implements IMyWCFService (generated when adding the service reference).
Also, I have a MyBusinessClassClient class that implements from the same IMyWCFService interface.
Thus, both MyWCFService and MyBusinessClient have the same method signatures. I have opted not to generate any async methods while generating the service client, because, If I do, I may need to implement so many unnecessary stuff generated by IMyWCFService in MyBusinessClient also.
Let’s assume that I have a method GetEmployee(int id) that returns an Employee object, defined in IMyWCFService. Thus both the classes MyWCFServiceClient and MyBusinessClient will have its implementations.
In my ViewModel, I have:
private void btnGetEmployee_Click()
{
ServiceAgent sa = new ServiceAgent ();
//this call/callback process the service call result
sa.GetEmployee(1673, (IAsyncResult ar) =>
{
Task<Employee> t1 = (Task<Employee>)ar;
Employee = t1.Result;
//do some other operation using the result
//do some UI updation also
});
}
//this property is bound a label in the view
private Employee _employee;
public Employee Employee
{
get
{
return _ employee;
}
set
{
_ employee = value;
OnPropertyChanged(() => Employee);
}
}
The ServiceAgent class is implemented as the following:
public class ServiceAgent
{
private IMyWcfService client;
public ProxyAgent()
{
//The call can go to either MyWCFServiceClient or
//MyBusinessClient depending on this setting
//client = new MyBusinessClient();
//OR
client = new MyWcfServiceClient();
}
public void GetEmployee(int id, AsyncCallback callback)
{
//My implementation to execute the service calls asynchronously using tasks
//I don’t want to use the complex async mechanism generated by wcf service reference ;)
Task<Employee> t = new Task<Employee>(()=>client.GetEmployee(id));
t.Start();
try
{
t.Wait();
}
catch (AggregateException ex)
{
throw ex.Flatten();
}
t.ContinueWith(task=>callback(t));
}
}
This is freezing my UI. I want to avoid that. Also I wonder whether this is a proper way for what I want to achieve. I have less experience with tasks/threads and callbacks, and hence I’d like to know whether I will have any issues I the future (threading/memory management etc).
#Ananth heh, I deleted the comment because on second glance I thought I was misreading the code. Generally speaking, when connecting to a web service, you should always treat the call as asynchronous because you can be dealing with excessive lag which would freeze whatever thread (typically the GUI thread). This is compounded if you need to make multiple WCF calls for a single GUI action. This is also worsened because your WCF interface is written like an asynchronous call but then lies and runs synchronously anyway. Definite cause for future confusion.
So I find it's best just to deal with the asynchronous model, but at least you can do the type-checking/casting/return handling within your WCF call. I did something similar, but instead of using synchronous calls, I would still use callbacks, but I'd have the IAsyncResult handled in the WCF call which would then cast it to my expected type and serve it back to the user.
public void GetEmployee(int id, Action<Employee> getEmployeeCompletedHandler)
{
Task<Employee> t = new Task<Employee>(()=>client.GetEmployee(id));
t.Start();
t.ContinueWith(task=>
{
if (getEmployeeCompletedHandler != null)
getEmployeeCompletedHandler(t1.Result);
});
}
Which makes your typical usage:
sa.GetEmployee(1673, result => this.Employee = result);
If you really want to maintain an synchronous model, then you can move work to a background thread (but that will still "asynchronous" from the GUI thread's perspective). At this point too, you may as well have your GetEmployee method be synchronous and return the value. This way it's obvious to the API consumer using it that there is no asynchronous operation:
public Employee GetEmployee(int id)
{
Task<Employee> t = new Task<Employee>(()=>client.GetEmployee(id));
t.Start();
try
{
t.Wait();
}
catch (AggregateException ex)
{
throw ex.Flatten();
}
return t.Result;
}
Then your calling code might look like:
//spawn a background thread to prevent the GUI from freezing
BackgroundThread.Spawn(() =>
{
this.Employee = sa.GetEmployee(1673);
});
Note, BackgroundThread is a custom class that you can make to wrap the creation/spawning of a background thread. I'll leave that implementation detail to you, but I find it's best just to have a managed wrapper for threading because it makes usage so much simpler and abstracts implementation details (using thread pool? new thread? BackgroundWorker? Who cares!)
Just a note, I haven't tried the synchronous usage for WCF calls I just posted above (I stick to a full asynchronous model like my first code sample), so I think it would work. (I still don't recommend doing it this way though!)