简体   繁体   中英

Running Tasks in parallel

I am failing to understand why this doesn't seem to run the tasks in Parallel:

var tasks = new Task<MyReturnType>[mbis.Length];

for (int i = 0; i < tasks.Length; i++)
{
     tasks[i] = CAS.Service.GetAllRouterInterfaces(mbis[i], 3);
}

Parallel.ForEach(tasks, task => task.Start());

By stepping through the execution, I see that as soon as this line is evaluated:

tasks[i] = CAS.Service.GetAllRouterInterfaces(mbis[i], 3);

The task starts. I want to add all the new tasks to the list, and then execute them in parallel.

If GetAllRouterInterfaces is an async method, the resulting Task will already be started (see this answer for further explanation).

This means that tasks will contain multiple tasks all of which are running in parallel without the subsequent call to Parallel.ForEach .

You may wish to wait for all the entries in tasks to complete, you can do this with an await Task.WhenAll(tasks); .

So you should end up with:

var tasks = new Task<MyReturnType>[mbis.Length];

for (int i = 0; i < tasks.Length; i++)
{
    tasks[i] = CAS.Service.GetAllRouterInterfaces(mbis[i], 3);
}

await Task.WhenAll(tasks);

Update from comments

It seems that despite GetAllRouterInterfaces being async and returning a Task it is still making synchronous POST requests (presumably before any other await ). This would explain why you are getting minimal concurrency as each call to GetAllRouterInterfaces is blocking while this request is made. The ideal solution would be to make an aynchronous POST request, eg:

await webclient.PostAsync(request).ConfigureAwait(false);

This will ensure your for loop is not blocked and the requests are made concurrently.

Further update after conversation

It seems you are unable to make the POST requests asynchronous and GetAllRouterInterfaces does not actually do any asynchronous work, due to this I have advised the following:

  1. Remove async from GetAllRouterInterfaces and change the return type to MyReturnType
  2. Call GetAllRouterInterfaces in parallel like so

     var routerInterfaces = mbis.AsParallel() .Select(mbi => CAS.Service.GetAllRouterInterfaces(mbi, 3)); 

I don't know if I understand you the right way.

First of all, if GetAllRouterInterfaces is returns a Task you have to await the result.

With Parallel.ForEach you can't await tasks like as it is, but you can do something similar like this:

public async Task RunInParallel(IEnumerable<TWhatEver> mbisItems)
{
    //mbisItems == your parameter that you want to pass to GetAllRouterInterfaces

    //degree of cucurrency
    var concurrentTasks = 3;

    //Parallel.Foreach does internally something like this:
    await Task.WhenAll(
        from partition in Partitioner.Create(mbisItems).GetPartitions(concurrentTasks)
        select Task.Run(async delegate
        {
            using (partition)
                while (partition.MoveNext())
                {
                    var currentMbis = partition.Current;
                    var yourResult = await GetAllRouterInterfaces(currentMbis,3);
                }
        }
       ));
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM