I have a series of async methods that I would like to execute simultaneously. Each of these methods return true or false in regards to if they execute successfully or not. Their results are also logged in our audit trail so that we can diagnose issues.
Some of my functions are not dependent on all of these methods executing successfully, and we fully expect some of them to fail from time to time. If they do fail, the program will continue to execute and it will merely alert our support staff that they need to correct the issue.
I'm trying to figure out what would be the best method for all of these functions to execute simultaneously, and yet have the parent function await them only after they have all begun to execute. The parent function will return False if ANY of the functions fail, and this will alert my application to cease execution.
My idea was to do something similar to:
public async Task<bool> SetupAccessControl(int objectTypeId, int objectId, int? organizationId)
{
using (var context = new SupportContext(CustomerId))
{
if (organizationId == null)
{
var defaultOrganization = context.Organizations.FirstOrDefault(n => n.Default);
if (defaultOrganization != null) organizationId = defaultOrganization.Id;
}
}
var acLink = AcLinkObjectToOrganiation(organizationId??0,objectTypeId,objectId);
var eAdmin = GrantRoleAccessToObject("eAdmin", objectTypeId, objectId);
var defaultRole = GrantRoleAccessToObject("Default", objectTypeId, objectId);
await acLink;
await eAdmin;
await defaultRole;
var userAccess = (objectTypeId != (int)ObjectType.User) || await SetupUserAccessControl(objectId);
return acLink.Result && eAdmin.Result && defaultRole.Result && userAccess;
}
public async Task<bool> SetupUserAccessControl(int objectId)
{
var everyoneRole = AddToUserRoleBridge("Everyone", objectId);
var defaultRole = AddToUserRoleBridge("Default", objectId);
await everyoneRole;
await defaultRole;
return everyoneRole.Result && defaultRole.Result;
}
Is there a better option? Should I restructure in any way? I'm simply trying to speed up execution time as I have a parent function that executes close to 20 other functions that are all independent of each other. Even at it's slowest, without async, it only takes about 1-2 seconds to execute. However, this will be scaled out to eventually have that parent call executed several hundred times (bulk insertions).
Async methods have a synchronous part which is the part before the first await of an uncompleted task is reached (if there isn't one then the whole method runs synchronously). That part is executed synchronously using the calling thread.
If you want to run these methods concurrently without parallelizing these parts simply invoke the methods, gather the tasks and use Task.WhenAll to await for all of them at once. When all tasks completed you can check the individual results:
async Task<bool> SetupUserAccessControlAsync(int objectId)
{
var everyoneRoleTask = AddToUserRoleBridgeAsync("Everyone", objectId);
var defaultRoleTask = AddToUserRoleBridgeAsync("Default", objectId);
await Task.WhenAll(everyoneRoleTask, defaultRoleTask)
return await everyoneRoleTask && await defaultRoleTask;
}
If you do want to parallelize that synchronous part as well you need multiple threads so instead of simply invoking the async method, use Task.Run to offload to a ThreadPool thread:
async Task<bool> SetupUserAccessControlAsync(int objectId)
{
var everyoneRoleTask = Task.Run(() => AddToUserRoleBridgeAsync("Everyone", objectId));
var defaultRoleTask = Task.Run(() => AddToUserRoleBridgeAsync("Default", objectId));
await Task.WhenAll(everyoneRoleTask, defaultRoleTask)
return await everyoneRoleTask && await defaultRoleTask;
}
If all your methods return bool you can gather all the tasks in a list, get the results from Task.WhenAll and check whether all returned true:
async Task<bool> SetupUserAccessControlAsync(int objectId)
{
var tasks = new List<Task<bool>>();
tasks.Add(AddToUserRoleBridgeAsync("Everyone", objectId));
tasks.Add(AddToUserRoleBridgeAsync("Default", objectId));
return (await Task.WhenAll(tasks)).All(_ => _);
}
Related
i'm trying to make this code works in async way, but i got some doubts.
public async Task<string> GetAsync(string inputA, string inputB)
{
var result = await AnotherGetAsync(inputA, inputB)
.ConfigureAwait(false);
return result.Enabled
? inputB
: string.Empty;
}
I got some string input collection, and i would like to run this method on them.
Then i would do Task.WhenAll, and filter for non empty strings.
But it won't be async as inside the method i'm already awaiting for the result right?
I assumed the real question is:
If a single item is awaited inside method A, will this run sequential if I use Task.WhenAll for a range of items calling method A?
They will be run simultaneous with Task.WhenAll.
Perhaps it is best explained with an example:
void Main()
{
Test().GetAwaiter().GetResult();
}
private async Task<bool> CheckEmpty(string input)
{
await Task.Delay(200);
return String.IsNullOrEmpty(input);
}
private async Task Test()
{
var list = new List<string>
{
"1",
null,
"2",
""
};
var stopwatch = new Stopwatch();
stopwatch.Start();
// This takes aprox 4 * 200ms == 800ms to complete.
foreach (var itm in list)
{
Console.WriteLine(await CheckEmpty(itm));
}
Console.WriteLine(stopwatch.Elapsed);
Console.WriteLine();
stopwatch.Reset();
stopwatch.Start();
// This takes aprox 200ms to complete.
var tasks = list.Select(itm => CheckEmpty(itm));
var results = await Task.WhenAll(tasks); // Runs all at once.
Console.WriteLine(String.Join(Environment.NewLine, results));
Console.WriteLine(stopwatch.Elapsed);
}
My test results:
False
True
False
True
00:00:00.8006182
False
True
False
True
00:00:00.2017568
If we break this down:
AnotherGetAsync(inputA, inputB)
Returns a Task. Using the await keyword implicitly returns another Task to the code calling GetAsync(string inputA, string inputB).
Using the await keyword will free up the current thread, which is what makes the code asynchronous.
once the Task returned by AnotherGetAsync(inputA, inputB) is complete, result will be set to that Task.Result.
The remainder of the method will then resume:
return result.Enabled
? inputB
: string.Empty;
Setting the Task.Result of the Task returned from GetAsync.
If GetAsync is to be run multiple times, you can use Task.WhenAll to wait for each resulting Task to complete:
Task.WhenAll(yourStringCollection.Select(s => GetAsync(s, "another string"));
Task.WhenAll itself returns another Task that will complete when all the GetAsync tasks have completed:
var strings = await Task.WhenAll(yourStringCollection.Select(s => GetAsync(s, "another string"));
var nonEmptyStrings = strings.Where(s => s != string.Empty);
No, it will be async. When you await a task inside an async method, the task will be awaited when the async method is invoked. Here is your example simplified, using explicit variables for the created tasks:
public async Task<string> GetAsync()
{
var innerTask = InnerGetAsync();
var result = await innerTask;
var stringResult = result.ToString();
return stringResult;
}
Later you create two tasks by invoking the GetAsync() method:
var task1 = GetAsync();
var task2 = GetAsync();
At this point the two tasks are running concurrently. Each one has invoked internally the InnerGetAsync() method, which means that the two innerTasks are also up and running, and awaited. Any code that follows the await will not run before the innerTask is completed.
The completion of each outer task (task1 and task2) is dependent on the completion of its innerTask. Any code that will await task1, will also implicitly await innerTask as well.
After creating the two tasks, you combine them with Task.WhenAll, then do something else, then await the combined task, and finally process the results:
Task<string[]> whenAllTask = Task.WhenAll(task1, task2);
DoSomethingElse();
string[] results = await whenAllTask;
ProcessResults(results);
It is important that the DoSomethingElse() method will run before the two tasks are completed. So at this point three things will be happening concurrently, the DoSomethingElse() and the two active tasks. The await inside the GetAsync() method is local to this method, and does not mean that the generated outer task will be automatically awaited upon creation. To process the results of task1 and task2 you must first await them as well.
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();
}
Basically I need to make a remote request using a vendor's .Net SDK for some information. Their SDK has no async implementations on their methods so I am trying to come up with something on my own. I bascially want to fire off this request to a synchronous method, and wait on it for only a certain amount of time. If the request takes too long, I need to act and report that down to the client in our web app.
I'm wondering if this is the best way to do this, or is there a better way? The code below is a service method that is called from a Controller action.
public async Task<bool> SignersAdded(string packageId)
{
var timeout = 5000;
var task = Task.Run(() =>
{
var package = _eslClient.GetPackage(new PackageId(packageId));
return package != null && package.Documents.Values.Any(x => x.Signatures.Any());
});
var stopwatch = Stopwatch.StartNew();
while (!task.IsCompleted)
{
if (stopwatch.ElapsedMilliseconds >= timeout)
return false;
}
return false;
}
Task.Wait has an overload that takes an int which defines timeout.
public Task<bool> SignersAdded(string packageId)
{
var timeout = 5000;
var task = Task.Run(() =>
{
var package = _eslClient.GetPackage(new PackageId(packageId));
return package != null && package.Documents.Values.Any(x => x.Signatures.Any());
});
if(!task.Wait(1000 /*timeout*/))
{
// timeout
return false;
}
return task.Result;
}
Your method doesn't await on anything, so it runs synchronously. Also, your while loop will spin the CPU, blocking the calling code until the task is complete.
A better approach might be this:
var task = Task.Run(/* your lambda */)
var finishedTask = await Task.WhenAny(Task.Delay(timeout), task);
return finishedTask == task;
This way we create a separate delay task for that time and we await until the first task is complete. This will run in a truly asynchronous manner - there is no while loop that will burn CPU cycles.
(The above assumes timeout is in milliseconds. If not, then use an overload to Delay taking a TimeSpan argument instead.)
You are correct: start a task that calls GetPackage. After that you can continue doing other things.
After a while when you need the result you can wait for the task to complete. However you don't have to do Task.Wait. It is much easier to use async / await.
To do this, you have to do three things:
Declare your function async
Instead of void return Task and instead of type TResult return Task<TResult>. You already did that.
Instead of waiting for the task to finish use await
Your function would look much simpler:
public **async** Task<bool> SignersAdded(string packageId)
{
var timeout = TimeSpan.FromSeconds(5);
var task = Task.Run(() =>
{
var package = _eslClient.GetPackage(new PackageId(packageId));
return package != null
&& package.Documents.Values
.Any(x => x.Signatures.Any());
});
// if desired you can do other things here
// once you need the answer start waiting for it and return the result:
return await Task;
}
if you have a function that returns TResult the async version of it returns Task<TResult>.
the return value of await Task<TResult> is TResult
However, if you want to be able to wait with a timeout you can do the following:
var tokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(1);
// cancel after 1 second
try
{
return await task.Run( () => ..., tokenSource.Token);
}
catch (OperationCanceledException exc)
{
// handle timeout
}
finally
{
// do necessary cleanup
}
The disadvantage of making your function async is that all callers also have to be async and all have to return Task or Task<TResult>. There is one exception:
An event handler can be async but may return void
Example:
private async void OnButton1_clicked(object sender, )
Look at the TaskCompletionSource and the CancellationToken class. Samples here: Timeout an async method implemented with TaskCompletionSource or How to cancel a TaskCompletionSource using a timout
I'm using .NET 4.0, so I can't use the async/await keywords.
After I laboriously set up tasks and continuations instead of just calling .Result, all I got for my efforts was a mess and it runs 46% slower on a workload of a few dozen HTTP GETs. (I get similar perf degradation if I call the workload in serial or in a Parallel loop)
What must I do to see any performance benefit?
//Slower code
public UserProfileViewModel GetAsync(Guid id)
{
UserProfileViewModel obj = null;//Closure
Task result = client.GetAsync(id.ToString()).ContinueWith(responseMessage =>
{
Task<string> stringTask = responseMessage.Result
.Content.ReadAsStringAsync();
Task continuation = stringTask.ContinueWith(responseBody =>
{
obj = JsonConvert
.DeserializeObject<UserProfileViewModel>(responseBody.Result);
});
//This is a child task, must wait before returning to parent.
continuation.Wait();
});
result.Wait();
return obj;
}
//Faster code
public UserProfileViewModel GetSynchr(Guid id)
{
//Asych? What's is that?
HttpResponseMessage response = client.GetAsync(id.ToString()).Result;
string responseBody = response.Content.ReadAsStringAsync().Result;
return JsonConvert.DeserializeObject<UserProfileViewModel>(responseBody);
}
You are using "async" methods but doing everything synchronously. That certainly won't be any better than doing everything synchronously with the synchronous methods.
Take a look at this:
public Task<UserProfileViewModel> GetAsync(Guid id)
{
var uri = id.ToString();
return client.GetAsync(uri).ContinueWith(responseTask =>
{
var response = responseTask.Result;
return response.Content.ReadAsStringAsync().ContinueWith(jsonTask =>
{
var json = jsonTask.Result;
return JsonConvert.DeserializeObject<UserProfileViewModel>(json);
});
}).Unwrap();
}
Notice how the method returns a Task and the continuations are returned from the method. This allows your method to return almost instantly, giving the caller a handle to the running work and whatever continuations need to happen. The returned task will only be complete once everything is done, and it's result will be your UserProfileViewModel.
The Unwrap method takes a Task<Task<UserProfileViewModel>> and turns it into a Task<UserProfileViewModel>.
Been trying to execute tasks sequentially but they are executed in a random order instead.
Appending .Unwrap after .ContinueWith doesn't help
Returning a Task of T from these methods instead of Task and assigning their result in the caller doesn't work either
Not sure about signature of my methods, whether they should contain async/await or not.
Sequencing tasks :
Task biographies = LoadArtistBiographies(apiKey);
Task blogs = LoadArtistBlogs(apiKey);
Task familiarity = LoadArtistFamiliarity(apiKey);
Task hottness = LoadArtistHottness(apiKey);
Task images = LoadArtistImages(apiKey);
await biographies.ContinueWith(b => blogs);
await blogs.ContinueWith(f => familiarity);
await familiarity.ContinueWith(h => hottness);
await hottness.ContinueWith(i => images);
await images;
Sample of executed methods :
private async Task LoadArtistBiographies(string apiKey)
{
var parameters = new ArtistBiographiesParameters();
parameters.SetDefaultValues();
parameters.ApiKey = apiKey;
parameters.Id = _artistId;
ArtistBiographies biographies = await Queries.ArtistBiographies(parameters);
ItemsControlBiographies.ItemsSource = biographies.Biographies;
}
The Queries.* methods are also asynchronous :
public static async Task<ArtistBlogs> ArtistBlogs(ArtistBlogsParameters parameters)
What is the correct syntax for chaining tasks that themselves are executing asynchronous tasks ?
If you want to execute the tasks in a specific order, you should await them directly:
await LoadArtistBiographies(apiKey);
await LoadArtistBlogs(apiKey);
await LoadArtistFamiliarity(apiKey);
await LoadArtistHottness(apiKey);
await LoadArtistImages(apiKey);
This will cause the second task (LoadArtistBlogs) to be scheduled after the first task completes.
Right now, the tasks are executing "in random order" because you've assigned them to Task instances, which allows each to be executed simultaneously.
That being said, I would actually recommend changing your methods around to returning the values, instead of assigning them to the datasource within the method:
private async Task<Biographies> LoadArtistBiographiesAsync(string apiKey)
{
var parameters = new ArtistBiographiesParameters();
parameters.SetDefaultValues();
parameters.ApiKey = apiKey;
parameters.Id = _artistId;
var bio = await Queries.ArtistBiographies(parameters);
return bio.Biographies;
}
You could then write these as:
ItemsControlBiographies.ItemsSource = await LoadArtistBiographiesAsync(apiKey);
// Other methods below, with await as this example
This makes the intent as the logic flows through the async methods a bit more clear, in my opinion.
Your example code will start executing all the tasks without waiting for each one to complete. It then waits for them to complete in order.
The key is that an async method starts when you call it. So if you don't want to start it yet, don't call the method yet:
await LoadArtistBiographies(apiKey);
await LoadArtistBlogs(apiKey);
await LoadArtistFamiliarity(apiKey);
await LoadArtistHottness(apiKey);
await LoadArtistImages(apiKey);
await will wait for the given task to complete, it will not start the task. Your Load*-methods all most likely start a task. All five tasks are running in an arbitrary order.
At the point when you get to await, your task may already has finished or not. It does not matter. You call ContinueWith on it, telling your task it should continue with this method once finished. This will return a new Task, on which you finally await.
Actually I've just found a way but without ContinueWith :
ArtistBiographies biographies = await LoadArtistBiographies(apiKey);
ItemsControlBiographies.ItemsSource = biographies.Biographies;
ArtistBlogs blogs = await LoadArtistBlogs(apiKey);
ItemsControlBlogs.ItemsSource = blogs.Blogs;
ArtistFamiliarity familiarity = await LoadArtistFamiliarity(apiKey);
ContentControlFamiliarity.Content = familiarity.artist;
ArtistHotttnesss hottness = await LoadArtistHottness(apiKey);
ContentControlHottness.Content = hottness.Artist;
ArtistImages images = await LoadArtistImages(apiKey);
ItemsControlImages.ItemsSource = images.Images;
Curious if someone could provide the answer using ContinueWith.