I'm wondering how to best handle this async await chain where multiple CPU bound methods need to be called sequentially after the async chain.
I've typed up a small example below.
I'm just trying to find out what the best performing / least side effecting pattern is. I want to make sure I'm not defeating the async benefits. My Filter methods do not access anything that is async and awaitable so to make them async means I would have to return Task.Run(() => Filter1(criterion)) in the calling method or something like await Task.Run(() => { return events; }); in the filter methods themselves. Which way to go for a best practice is the question. This is where most discussions stop so a full example and recommendation would be nice.
Are there any 4.5 async await gurus out there who can give good advice?
namespace test
{
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;
public class sampleController : ApiController
{
private Service _service;
public sampleController()
{
_service = new Service();
}
public async Task<HttpResponseMessage> SomeTask(DiagnosesSearchCriterion criterion)
{
return Request.CreateResponse<IEnumerable<Diagnosis>>(HttpStatusCode.OK, await _service.GetDiagnosesByGroup(criterion));
}
}
public class Service
{
private Repository _repository;
public Service()
{
_repository = new Repository();
}
public async Task<IEnumerable<Diagnosis>> GetDiagnosis(DiagnosesSearchCriterion criterion)
{
System.IO.Stream events = await _repository.GetEvents(criterion);
// Will these block? Should they be async? They are CPU bound...
// how to best handle this, they need to be called sequentially in most cases.
events = Filter1(criterion, events);
events = Filter2(criterion, events);
return new Diagnosis[]{};
}
public System.IO.Stream Filter1(DiagnosesSearchCriterion criterion, System.IO.Stream events)
{
// CPU bound PLINQ and Parallel filtering logic here.....
return events;
}
public System.IO.Stream Filter2(DiagnosesSearchCriterion criterion, System.IO.Stream events)
{
// CPU bound PLINQ and Parallel filtering logic here.....
// ....
return events;
}
}
public class Repository
{
public async Task<System.IO.Stream> GetEvents(DiagnosesSearchCriterion criterion)
{
WebClient wc = new WebClient();
return await wc.OpenReadTaskAsync("http://www.blah.com/stuff");
}
}
}
On the server side, your primary benefit from async is scalability - that is, the thread pool thread is freed up from handling a request if the request is just waiting for some I/O to complete.
In this case (where your methods are CPU-bound), there's no benefit from making them async. You'll still be taking up a thread pool thread (and adding a small amount of overhead) by using Task.Run. Since they should be executed sequentially, the easiest way to do this is to invoke them synchronously, just like your code is currently doing.
I think this scenario is addressed in the excellent Async in ASP.NET video. Note that this same situation on the client side would be handled differently. On the client side, your primary benefit from async is responsiveness, so it would make sense to toss CPU work into a thread pool thread (Task.Run), since that would free up the UI thread.
(As a side note, it's usually not a good idea to do parallel processing on a server, unless you're sure your user count will be quite low).
Related
Let's say I have an interface:
interface ILoader()
{
Task<Data> LoadAsync(int id);
}
I have two implementations of this interface:
class NiceDataBase : ILoader
{
public async Task<Data> LoadAsync(int id)
{
//this database has async way of fetching the data
return await NiceDataBaseDriver.LoadDataAsync(id);
}
}
class NotNiceDataBase : ILoader
{
public async Task<Data> LoadAsync(int id)
{
//this database's driver does not have aysnc methods, because it's old or done poorly.
return await Task.Run(() => NotNiceDataBaseDriver.LoadData(id));
}
}
The NotNiceDataBase doesn't provide real async way to load the data, that's why I actually run it on a new thread, which, as I understand, is not really async, because real async does not use a new thread. Is my implementation of NotNiceDataBase a good one then? It kind of cheats the user to think that he is running a real async operation.
What is the optimal way to deal with such situation? I think that the client of NotNiceDataBase should be completely aware what he is doing, otherwise he can't control performance of hiw application well.
My interface could have additional Load method. But in such case, what's the real benefit here? Still LoadAsync of NotNiceDataBase would need to have some implementation. Throwing NotImplementedException is never a good solution I think.
As you know async is an implementation detail and you can not define async on interfaces. So all you have to do is
public class NotNiceDataBase : ILoader
{
public Task<Data> LoadAsync(int id)
{
//this database's driver does not have aysnc methods, because it's old or done poorly.
var result = NotNiceDataBaseDriver.LoadData(id);
return Task.FromResult(result);
}
}
Simply run the method synchronously and return a completed task
e.g.
class NotNiceDataBase : ILoader
{
public Task<Data> LoadAsync(int id)
{
var data = NotNiceDataBaseDriver.LoadData(id);
return Task.From(data);
}
}
You say
I think that the client of NotNiceDataBase should be completely aware
what he is doing, otherwise he can't control performance of hi(s)
application well.
I disagree, if performance is an issue and if they've identified that it's the call to NotNiceDataBase then they can do something about it.
elaboration
Time worrying about how the client is using the interface is almost certainly1 time wasted, and isn't even "premature optimization" but is existential angst over someone else's unknown optimization needs.
1Unless you are your own client, then it's still probably time wasted.
I'm building a RESTful web api that is going to fire off several tasks, wait for them to complete then return some data. But I keep having problems with it seeming to hang when waiting. I scaled it down the the most basic:
[Authorize]
public class ValuesController : ApiController
{
public IEnumerable<string> Get([FromUri]bool enableWait)
{
Task t = Silly(enableWait);
t.Wait();
return new string[] { "value1", "value2" };
}
private async Task Silly(bool delay)
{
if (delay)
await Task.Delay(1);
}
}
If I pass false it returns without any issue. If I return true, it should wait for 1 millisecond (plus Task/Asnyc overhead) then return. However it just hangs there.
I'm running this on the IIS Express that comes with VS 2017. Any clues?
When implementing async/await you need to make sure you are implementing it all the way through. Your controller action needs to be async, and it needs to await your Silly() method:
public async Task<IEnumerable<string>> Get()
{
return await Silly();
}
You also have to make sure your Silly() method is calling the proper async methods it needs, making sure you are await-ing them.
I've been banging my head against a wall for two days now, and frankly I'm annoyed with myself because I just can't seem to get it.
I'm in a webapi context. During this request I need to send some data to one of our other systems, this system is slow to return, due to heavy calculations and multiple database saves etc etc. I need to log the result of this operation, regardless of whether it is successful or not. But I don't want to wait around for it to finish.
I've read that I should be async await all the way from top to bottom. I would have to convert numerous methods if I decided to do this, as I'm already 3 or 4 methods deep, which I fear would branch out even more.
What are my options here? If I go async await all the way down, what do I do with the methods higher up the stack, like my WebApi controllers?
Here is my code, I've tried to thin it down as much as I can. Right now I'm using Task.Result() in the method PushResult(). Which to my understanding is blocking the async? This code works in that the request gets sent. But the TestLog is always last, not first. Therefore not async.
//I'm in a public service and referenced twice
private void MyEndProcess()
{
// other stuff
_vendorPushService.PushResult(); // This could take a while and I have to wait for it!
_logService.PostLog(LogType.TestLog, "Test");
}
//I'm referenced above and somewhere else in the code base
public void PushResult()
{
ExternalResultModel externalResultModel = _resultService.GetExternalResultModel();
PushedResultModel pushedResult = new PushedResultModel();
try
{
pushedResult = _vendorRequestService.PushResultAsync(externalResultModel).Result;
}
catch (Exception ex)
{
pushedResult.Success = false;
}
if (pushedResult.Success)
{
_logService.PostLog(LogType.SuccessLog, pushedResult.Message);
}
else
{
_logService.PostLog(LogType.FailedLog, pushedResult.Message);
}
}
public async Task<PushedResultModel> PushResultAsync(ExternalResultModel externalResultModel)
{
// setup the requestMessage
HttpResponseMessage responseMessage = await _httpRequestService
.SendRequest(requestMessage)
.ConfigureAwait(false);
return new PushedResultModel
{
Success = responseMessage.IsSuccessStatusCode,
Message = await responseMessage.Content.ReadAsStringAsync()
};
}
public class HttpRequestService : IHttpRequestService
{
private readonly HttpClient _httpClient;
public HttpRequestService(IHttpClientAccessor httpClientAccessor)
{
_httpClient = httpClientAccessor.HttpClient;
}
public async Task<HttpResponseMessage> SendRequest(HttpRequestMessage requestMessage)
{
HttpResponseMessage httpResponseMessage = await _httpClient.SendAsync(requestMessage).ConfigureAwait(false);
return httpResponseMessage;
}
}
You should implement async await all the way from top to bottom.
If I go async await all the way down, what do I do with the methods higher up the stack, like my WebApi controllers?
Just make your controller actions async like this:
[RoutePrefix("api")]
public class PresidentsController : ApiController
{
[Route("presidents")]
public async Task<IHttpActionResult> GetPresidents()
{
await Task.Delay(TimeSpan.FromSeconds(10)).ConfigureAwait(false);
return Ok();
}
}
It's easiest way to implement async methods. Even if it will add some work to change everything to async it will benefit in future, because You will avoid many problem with async code.
If you absolutly HAVE to use async method in synchronous methods make it block in ONE place, like this:
public void MySyncMethod()
{
try
{
this.MyAsyncMethod().Wait();
}
catch (Exception exception)
{
//omited
}
}
private async Task MyAsyncMethod()
{
await AsyncLogic().ConfigureAwait(false);
}
But i don't recommend it. You should just use async await all the way to controller action.
In your comment you said you want to process a task in the background and not make the client calling your API wait. To do that, you don't really need to use async/await.
Try this:
private void MyEndProcess()
{
// other stuff
Task.Run(_vendorPushService.PushResult()).ConfigureAwait(false); //fire and forget
_logService.PostLog(LogType.TestLog, "Test");
}
The Task.Run will start the task, and the ConfigureAwait(false) tells it that it does not need to resume on the same context that we're currently on (meaning that the context can close before the task is finished - i.e. the response can be sent back without waiting for the task to finish).
You will get a compiler warning that you're not awaiting Task.Run, but that's what you want.
Keep in mind that when you do this, HttpContext.Current will not be available inside PushResult.
I'm starting with Async MVC and I would like to know which is the main difference between this two implementations of an AsyncController.
The first one is using the ViewNameAsync and ViewNameCompleted implementation:
public class HomeController : AsyncController
{
// ... Manager declaration ...
public void IndexAsync()
{
AsyncManager.OutstandingOperations.Increment();
Manager.ExpensiveOperationCompleted += () =>
{
Debug.WriteLine("Expensive operation completed.");
AsyncManager.OutstandingOperations.Decrement();
};
Manager.ExpensiveOperationAsync();
}
public ActionResult IndexCompleted()
{
return View();
}
}
And the second one is using async-await implementation:
public class HomeController : Controller
{
// ... Manager declaration ...
public async Task<ActionResult> Index()
{
await Manager.ExpensiveOperation();
return View();
}
}
The "main difference" is that the Async/Completed approach is using an outdated and less maintainable way to do asynchronous request handling.
The main reason you do it this way (await the same line) is to tell the thread resource manager that it can balance the thread with other threads. This will allow other threads with higher priority to finish. You can also reference your expensive operation as a variable and then later down the code block, await it there. This will allow your expensive operation to do it's thing while the rest of the code executes. Basically like using Thread.Join.
Using a completed event isn't going to work in this case, considering your IndexAsync will return before your expensive operation completes and may cause undesired results if a recycle happens.
I am using MvvmLight and have implemented communication between some of my ViewModels using the MessengerInstance.Send(...) method. It works great!
Recently, though, I have moved from using Synchronous methods to async methods to retrieve data and it looks like this breaks messaging (probably because it executes on a different thread). For example:
public ICommand SomeCommand { get { return new RelayCommand(DoSomething); } }
private async void DoSomething(object obj)
{
//Used to be SomeWcfService.DoSomething(); with some logic afterward
await SomeWcfService.DoSomethingAsync().ContinueWith(task => { //Some logic after method completes });
MessengerInstance.Send(SomeDataToSend, MessageIdentifer.DoSomething);
}
Instead of using a continuation, just put it after the await:
private async void DoSomething(object obj)
{
//Used to be SomeWcfService.DoSomething(); with some logic afterward
var result = await SomeWcfService.DoSomethingAsync();
// .ContinueWith(task => { //Some logic after method completes });
// use result here!
MessengerInstance.Send(SomeDataToSend, MessageIdentifer.DoSomething);
}
If there is no result returned from DoSomethingAsync, you can just leave out the result, and put your code in place.
The continuation, as you wrote it, will not run on the same synchronization context. The await keyword is actually asynchronously waiting your continuation, not the async method from WCF, as well.
If your "some logic" is asynchronous, you can use await within that code, as well.