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).
The correct way to await multiple tasks is the Task. WhenAll method: await Task. WhenAll(first, second); . Then you can await them individually to get their results, because you know that all have completed successfully.
If it is some trivial operation that executes quickly, then you can just call it synchronously, without the need for await . But if it is a long-running operation, you may need to find a way to make it asynchronous.
Now, the Parallel. Invoke does not wait for the methods to finish and the spinner is instantly off.
await hides all this complexity from you, and it allows you to await the same task in ten different places (very useful for e.g. asynchronous lazy initialization). can I be assured that the method pointed by task wont be executed twice even if the task is running or ran already ? @BilalFazlani Yes, you can.
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(_ => _);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With