I have multiple async methods that return the same type from different OAuth based REST Api calls.
If I call one directly, I can get the data back:
//Call a specific provider
public async Task<List<Contacts>> Get()
{
return await Providers.SpecificProvider.GetContacts();
}
However, if I try to loop through multiple accounts, the object is returning before the AWAIT finishes:
//Call all providers
public async Task<List<Contacts>> Get()
{
return await Providers.GetContactsFromAllProviders();
}
public async Task<List<Contacts>> GetContactsFromAllProviders()
{
var returnList = new List<Contacts>();
//Providers inherits from List<>, so it can be enumerated to trigger
//all objects in the collection
foreach (var provider in Providers)
{
var con = await provider.GetContacts();
returnList.Add(con);
}
return returnList;
}
I'm new to async and am probably missing something simple
The code you have provided will call the web service for each provider one by one in a serial fashion. I do not see how your statement the object is returning before the AWAIT finishes can be true because inside the loop the call to GetContacts is awaited.
However, instead of calling the providers serially you can call them in parallel which in most cases should decrease the execution time:
var tasks = providers.Select(provider => provider.GetContacts());
// Tasks execute in parallel - wait for them all to complete.
var result = await Task.WhenAll(tasks);
// Combine the returned lists into a single list.
var returnList = result.SelectMany(contacts => contacts).ToList();
Related
How i can get an iterator for use in Task.WaitAll to wait for all task exist in customList with lowest overhead or lines of code ?
public class Custom
{
public Task task;
public int result;
}
public class Main
{
void doSomething()
{
List<Custom> customList=new List<>();
//customList.Add(custom1,2,3,4,5.....);
Task.WaitAll(????)
}
}
Tasks aren't threads. They represent the execution of a function, so there is no point in storing them and their results as classes. Getting the results of multiple tasks is very easy if you use Task.WhenAll.
There is no reason to wrap the task in a class. You can think of Task itself as the wrapper over a thread invocation and is result.
If you want to retrieve the contents from multiple pages, you can use LINQ a single HttpClient to request the content from many pages at once. await Task.WhenAll will return the results from all tasks :
string[] myUrls=...;
HttpClient client=new HttpClient();
var tasks=myUrls.Select(url=>client.GetStringAsync(url));
string[] pages=await Task.WhenAll(tasks);
If you want to return more data, eg check the contents, you can do so inside the Select lambda:
var tasks=myUrls.Select(async url=>
{
var response=await client.GetStringAsync(url);
return new {
Url=url,
IsSensitive=response.Contains("sensitive"),
Response=response
};
});
var results =await Task.WhenAll(tasks);
Results contains the anonymous objects generated inside Select. You can iterate or query them just like any other array, eg:
foreach(var result in results)
{
Console.WriteLine(result.Url);
}
Essentially I'm trying to be able to do this:
var thingTasks = thingFactory.GetMyThings();
// ...
var things = await thingTasks;
I'm trying to start from a list of objects, iterate through that list making an async call for each one, and returning the set of results in an await-able way so the consumer can choose when to await it. GetMyThings itself uses await before generating the list, so it needs to be async itself and is something like:
public async Task<List<Thing>> GetMyThings() {
var thingMakers = await GetThingMakers();
var things = thingMakers.Select(async thing => await thing.GetAsync());
return things;
}
The basic idea is that I have some await lines, then after that I use the results of those lines to generate a list and generating each item also requires an async call. I'm trying to avoid blocking within the method (e.g. .Result) and instead pass that responsibility/opportunity back to the caller. Basically, start the tasks in the list but not await them. This naturally makes me want to return Task<List<Thing>> or 'List>`.
The closest I got was return Task.WhenAll(things) but that didn't work (it needed to be Task<Task<Thing[]>> and await await GetMyThings(). Alternatively, return Select(...) returning a Task<List<Task<Thing>>> and needing a await Task.WhenAll(await GetMyThings()) on the consuming side.
In both cases one needs double await statements to realize the list. I'm thinking it's impossible but is there a way to avoid the double await?
Use Task.WhenAll to await all task at once. This way you will run each GetAsync approximately at the same time. So :
Start all task
Await all
Return task's results
Like this :
public async Task<List<Thing>> GetMyThings()
{
var thingMakers = await GetThingMakers();
var tasks = thingMakers.Select(thing => thing.GetAsync());
var things = await Task.WhenAll(tasks);
return things.ToList();
}
If you want to make the inner tasks await-able outside, you need to actually return them:
public async Task<List<Task<Thing>>> GetMyThings() {
var thingMakers = await GetThingMakers();
var things = thingMakers.Select(thing => thing.GetAsync());
return things.ToList();
}
You can then use this call like this:
List<Task<Thing>> thingTasks = await GetMyThings();
await Task.WhenAll(thingTasks);
List<Thing> = thingTasks.Select(t => t.Result).ToList();
Or even:
List<Thing> things = await GetMyThings()
.ContinueWith(async r =>
{
await Task.WhenAll(r);
return r.Select(r => r.Result).ToList();
});
I am return a list which basically calls the two async operations:
[HttpPost]
public ActionResult List(DataSourceRequest command, ProductListModel model)
{
var categories = _productService.GetAllProducts(model.SearchProductName,
command.Page - 1, command.PageSize);
var gridModel = new DataSourceResult
{
Data = categories.Select(async x =>
{
var productModel = x.ToModel();
var manufacturer = await _manufacturerService.GetManufacturerById(x.ManufacturerId);
var category = await _categoryService.GetCategoryById(x.CategoryId);
productModel.Category = category.Name;
productModel.Manufacturer = manufacturer.Name;
return productModel;
}),
Total = categories.TotalCount
};
return Json(gridModel);
}
It is an ajax request (from client side) but on front-end it never returns. Is there any a deadlock ?
Building up my answer from several comments and #usr's answer:
Data in the code above is actually IEnumerable<Task<ProductModel>>, not IEnumerable<ProductModel>. This is because the lambda passed to Select is async.
Most likely, the JSON serializer is going over this structure and enumerating the properties on the Task<ProductModel> instances, including Result.
I explain on my blog why accessing Result will cause a deadlock in this case. In a nutshell, it's because the async lambda will attempt to resume execution on the ASP.NET request context after its await. However, the ASP.NET request context is blocked on the call to Result, locking a thread inside that request context until the Task<T> completes. Since the async lambda cannot resume, it cannot complete that task. So both things are waiting for each other, and you get a classic deadlock.
There are some suggestions to use await Task.WhenAll, which I would normally agree with. However, in this case you're using Entity Framework and got this error:
A second operation started on this context before a previous asynchronous operation completed.
This is because EF cannot perform multiple asynchronous calls concurrently within the same db context. There are a couple ways around this; one is to use multiple db contexts (essentially multiple connections) to do the calls concurrently. IMO a simpler way is to make the asynchronous calls sequentially instead of concurrently:
[HttpPost]
public async Task<ActionResult> List(DataSourceRequest command, ProductListModel model)
{
var categories = _productService.GetAllProducts(model.SearchProductName,
command.Page - 1, command.PageSize);
var data = new List<ProductModel>();
foreach (var x in categories)
{
var productModel = x.ToModel();
var manufacturer = await _manufacturerService.GetManufacturerById(x.ManufacturerId);
var category = await _categoryService.GetCategoryById(x.CategoryId);
productModel.Category = category.Name;
productModel.Manufacturer = manufacturer.Name;
data.Add(productModel);
}
var gridModel = new DataSourceResult
{
Data = data,
Total = categories.TotalCount
};
return Json(gridModel);
}
The way to debug is is to pause the debugger during the hang. There you will find that some serializer is blocking on Task.Result or similar.
You fill the Data property with a IEnumerable<Task>. The serializer probably calls Result at some point and that's the classic ASP.NET deadlock. You probably should wrap the Select call with await Task.WhenAll(x.Select(...)).
And even then it might be unsafe to concurrently run those lambdas.
I have a list of objects that I need to run a long running process on and I would like to kick them off asynchronously, then when they are all finished return them as a list to the calling method. I've been trying different methods that I have found, however it appears that the processes are still running synchronously in the order that they are in the list. So I am sure that I am missing something in the process of how to execute a list of tasks.
Here is my code:
public async Task<List<ShipmentOverview>> GetShipmentByStatus(ShipmentFilterModel filter)
{
if (string.IsNullOrEmpty(filter.Status))
{
throw new InvalidShipmentStatusException(filter.Status);
}
var lookups = GetLookups(false, Brownells.ConsolidatedShipping.Constants.ShipmentStatusType);
var lookup = lookups.SingleOrDefault(sd => sd.Name.ToLower() == filter.Status.ToLower());
if (lookup != null)
{
filter.StatusId = lookup.Id;
var shipments = Shipments.GetShipments(filter);
var tasks = shipments.Select(async model => await GetOverview(model)).ToList();
ShipmentOverview[] finishedTask = await Task.WhenAll(tasks);
return finishedTask.ToList();
}
else
{
throw new InvalidShipmentStatusException(filter.Status);
}
}
private async Task<ShipmentOverview> GetOverview(ShipmentModel model)
{
String version;
var user = AuthContext.GetUserSecurityModel(Identity.Token, out version) as UserSecurityModel;
var profile = AuthContext.GetProfileSecurityModel(user.Profiles.First());
var overview = new ShipmentOverview
{
Id = model.Id,
CanView = true,
CanClose = profile.HasFeatureAction("Shipments", "Close", "POST"),
CanClear = profile.HasFeatureAction("Shipments", "Clear", "POST"),
CanEdit = profile.HasFeatureAction("Shipments", "Get", "PUT"),
ShipmentNumber = model.ShipmentNumber.ToString(),
ShipmentName = model.Name,
};
var parcels = Shipments.GetParcelsInShipment(model.Id);
overview.NumberParcels = parcels.Count;
var orders = parcels.Select(s => WareHouseClient.GetOrderNumberFromParcelId(s.ParcelNumber)).ToList();
overview.NumberOrders = orders.Distinct().Count();
//check validations
var vals = Shipments.GetShipmentValidations(model.Id);
if (model.ValidationTypeId == Constants.OrderValidationType)
{
if (vals.Count > 0)
{
overview.NumberOrdersTotal = vals.Count();
overview.NumberParcelsTotal = vals.Sum(s => WareHouseClient.GetParcelsPerOrder(s.ValidateReference));
}
}
return overview;
}
It looks like you're using asynchronous methods while you really want threads.
Asynchronous methods yield control back to the calling method when an async method is called, then wait until the methods has completed on the await. You can see how it works here.
Basically, the only usefulness of async/await methods is not to lock the UI, so that it stays responsive.
If you want to fire multiple processings in parallel, you will want to use threads, like such:
using System.Threading.Tasks;
public void MainMethod() {
// Parallel.ForEach will automagically run the "right" number of threads in parallel
Parallel.ForEach(shipments, shipment => ProcessShipment(shipment));
// do something when all shipments have been processed
}
public void ProcessShipment(Shipment shipment) { ... }
Marking the method as async doesn't auto-magically make it execute in parallel. Since you're not using await at all, it will in fact execute completely synchronously as if it wasn't async. You might have read somewhere that async makes functions execute asynchronously, but this simply isn't true - forget it. The only thing it does is build a state machine to handle task continuations for you when you use await and actually build all the code to manage those tasks and their error handling.
If your code is mostly I/O bound, use the asynchronous APIs with await to make sure the methods actually execute in parallel. If they are CPU bound, a Task.Run (or Parallel.ForEach) will work best.
Also, there's no point in doing .Select(async model => await GetOverview(model). It's almost equivalent to .Select(model => GetOverview(model). In any case, since the method actually doesn't return an asynchronous task, it will be executed while doing the Select, long before you get to the Task.WhenAll.
Given this, even the GetShipmentByStatus's async is pretty much useless - you only use await to await the Task.WhenAll, but since all the tasks are already completed by that point, it will simply complete synchronously.
If your tasks are CPU bound and not I/O bound, then here is the pattern I believe you're looking for:
static void Main(string[] args) {
Task firstStepTask = Task.Run(() => firstStep());
Task secondStepTask = Task.Run(() => secondStep());
//...
Task finalStepTask = Task.Factory.ContinueWhenAll(
new Task[] { step1Task, step2Task }, //more if more than two steps...
(previousTasks) => finalStep());
finalStepTask.Wait();
}
I have a simple MVC controller which I've made async which gets data from a web service using await Task.WhenAll. I would like to cache the results of these calls so I don't have to call the API all the time. At the moment I'm caching the results in the view controller when I get the response but ideally I would like the method I'm calling, which calls the API, to handle the caching. The problem is that method doesn't have access to the results yet as it's asynchronous and just returns a task.
Is it possible to have another method which caches the results once they're returned?
public async Task<ActionResult> Index()
{
// Get data asynchronously
var languagesTask = GetDataAsync<List<Language>>("languages");
var currenciesTask = GetDataAsync<List<Currency>>("currencies");
await Task.WhenAll(languagesTask, currenciesTask);
// Get results
List<Language> languages = languagesTask.Result;
List<Currency> currencies = currenciesTask.Result;
// Add results to cache
AddToCache("languages", languages);
AddToCache("currencies", currencies);
// Add results to view and return
ViewBag.languages = languages;
ViewBag.currencies = currencies;
return View();
}
public async Task<T> GetDataAsync<T>(string operation)
{
// Check cache for data first
string cacheName = operation;
var cacheData = HttpRuntime.Cache[cacheName];
if (cacheData != null)
{
return (T)cacheData;
}
// Get data from remote api
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri("https://myapi.com/");
var response = await client.GetAsync(operation);
// Add result to cache
//...
return (await response.Content.ReadAsAsync<T>());
}
}
As long as your cache implementation is in-memory, you can cache the tasks themselves rather than the task results:
public Task<T> GetDataAsync<T>(string operation)
{
// Check cache for data first
var task = HttpRuntime.Cache[operation] as Task<T>;
if (task != null)
return task;
task = DoGetDataAsync(operation);
AddToCache(operation, task);
return task;
}
private async Task<T> DoGetDataAsync<T>(string operation)
{
// Get data from remote api
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri("https://myapi.com/");
var response = await client.GetAsync(operation);
return (await response.Content.ReadAsAsync<T>());
}
}
This approach has the added benefit that if multiple HTTP requests try to get the same data, they'll actually share the task itself. So it shares the actual asynchronous operation instead of the result.
However, the drawback to this approach is that Task<T> is not serializable, so if you're using a custom disk-backed cache or a shared cache (e.g., Redis), then this approach won't work.
Bit late to answer this but I think i can improve on Stevens answer with an open source library called LazyCache that will do this for you in a couple of lines of code. It was recently updated to handle caching Tasks in memory for just this sort of situation. It is also available on nuget.
Given your get data method is like so:
private async Task<T> DoGetDataAsync<T>(string operation)
{
// Get data from remote api
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri("https://myapi.com/");
var response = await client.GetAsync(operation);
return (await response.Content.ReadAsAsync<T>());
}
}
Then your controller becomes
public async Task<ActionResult> Index()
{
// declare but don't execute a func unless we need to prime the cache
Func<Task<List<Language>>> languagesFunc =
() => GetDataAsync<List<Currency>>("currencies");
// get from the cache or execute the func and cache the result
var languagesTask = cache.GetOrAddAsync("languages", languagesFunc);
//same for currencies
Func<Task<List<Language>>> currenciesFunc =
() => GetDataAsync<List<Currency>>("currencies");
var currenciesTask = cache.GetOrAddAsync("currencies", currenciesFunc);
// await the responses from the cache (instant) or the api (slow)
await Task.WhenAll(languagesTask, currenciesTask);
// use the results
ViewBag.languages = languagesTask.Result;
ViewBag.currencies = currenciesTask.Result;
return View();
}
It has built in locking by default so the cacheable method will only execute once per cache miss, and it uses a lamda so you can do "get or add" in one go. It defaults to 20 minutes sliding expiration but you can set whatever caching policy you like on it.
More info on caching tasks is in the api docs and you may find the sample webapi app to demo caching tasks useful.
(Disclaimer: I am the author of LazyCache)
I suggest you to use MemoryCache
MemoryCache cache = MemoryCache.Default;
string cacheName = "mycachename";
if cache.Contains(cacheName) == false || cache[cacheName] == null)
{
// load data
var data = await response.Content.ReadAsAsync<T>();
// save data to cache
cache.Set(cacheName, data, new CacheItemPolicy() { SlidingExpiration = DateTime.Now.AddDays(1).TimeOfDay });
}
return cache[cacheName];