6

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).

3
  • I used to use Parallel.Invoke for this stuff. Commented Oct 5, 2015 at 15:56
  • 3
    You probably want to look at Task.WhenAll. For the result values, it's hard to tell without knowing half of the types involved... Commented Oct 5, 2015 at 15:56
  • Luckily, if I don't need to pass on the values to the parent (or await them for use in another function), all the functions will return a true or false value. Commented Oct 5, 2015 at 15:59

1 Answer 1

7

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(_ => _); } 
Sign up to request clarification or add additional context in comments.

5 Comments

I'm not worried about them executing entirely concurrently, I simply want to ensure that each one executes as quickly as possible without blocking the execution of the next call.
@Jdsfighter These sentences contradict each other. If you want the second operation to start as soon as possible then you need the first operation to release the calling thread as soon as possible. This means offloading even the synchronous part to a different thread.
Every single one of the calls performs a series of database functions. I merely want to ensure that I can perform these functions quickly without blocking the calling thread for extended periods of times (like synchronous database calls would).
@Jdsfighter If you don't use Task.Run the calling thread will run these synchronous parts one by one. If that is an extended period of time depends on your code. Task.Run offloads these parts to a ThreadPool thread and so the calling thread does as little as possible.
I finally just got up and went and talked to one of our senior developers and he finally helped me grasp it a bit. It seems that I often conflate asynchronous and parallelism in my head. He also mentioned that I should probably avoid putting operations in separate threads unless they're fairly CPU intensive.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.