AngelBlueSky
AngelBlueSky

Reputation: 575

Run Multiple Tasks (Variable Number) in parallel and continue when all have finished

I need to start a "number" of tasks (variable but less than 10) not in parallel, and wait for them all to finish, getting from each the result. I'm getting the result from each of them, saving in a list and then using it in the end.

Here's my code, and it's working but I think there gotta be a cleaner way to do that.

CAUSING THE NUMBER OF TASKS

List<String> Arguments = new List<String> { "AA", "BB", "CC" }; 

List<String> ResultList = new List<String>();  

//**AT LEAST I'VE GOT ONE**

Task<String> Tasks = Task<String>.Factory.StartNew(() =>
{
    return DoSomething(Arguments[0]);
});

ResultList.Add(Tasks.Result);

for (Int32 i = 1; i < Arguments.Count; i++)
{
    ResultList.Add(Tasks.ContinueWith<String>(Result =>
    {
        return DoSomething(Arguments[i]);

    }).Result);
}

//**DO I NEED THIS?? It's working even without!!**
//Tasks.Wait();

for (Int32 i = 0; i < ResultList.Count; i++)
{
    textBox1.AppendText(ResultList[i] + Environment.NewLine + Environment.NewLine);
}

Upvotes: 5

Views: 3123

Answers (4)

Yuval Itzchakov
Yuval Itzchakov

Reputation: 149656

Your entire code, as of now, runs synchronously. You're creating a Task then actively blocking on it, using Task.Result.

If you truly understand what parallelism is about, and you really need to execute as many tasks as you have arguments in your list, then you can offload all of them and then asynchronously wait (this is the key, being asynchronous) as they complete:

var arguments = new[] { "A", "B", "C" };
var tasks = arguments.Select(argument => Task.Run(() => DoSomething(argument))).ToList();

while (tasks.Count > 0)
{
    var finishedTask = await Task.WhenAny(tasks);
    textBox1.AppendText(string.Format("{0}{1}{1}", finishedTask.Result,
                                                   Environment.NewLine));

    tasks.Remove(finishedTask);
}

Edit: From your comments:

I'm calling a web api (Not mine) that doesn't allow me multiple call in parallel, that's why I need to wait for each task to finish

Then you don't need a thread for each argument at all. Two things:

  1. You can make the call entirely synchronous, as you have to wait for the completion of each call. Using threadpool threads for that is merely useless
  2. You can make asynchronous calls without needing to use threads at all. Look into HttpClient and its XXXAsync methods (such as GetAsync).

Upvotes: 0

Ewan
Ewan

Reputation: 1320

I think this is what you are attempting to do : ie start a whole load of parallel tasks and wait for them all to complete before proceeding

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace UnitTestProject2
{
    class Class4
    {
        public void run()
        {
            List<String> Arguments = new List<String> { "AA", "BB", "CC" };
            List<Task<String>> tasks = new List<Task<string>>();

            foreach (string arg in Arguments)
            {
                    tasks.Add(
                        this.DoSomething(arg)
                        .ContinueWith(t => this.DoSomething(t.Result))
                        .Unwrap<string>()
                    );
            }

            Task.WaitAll(tasks.ToArray());

            foreach(Task<string> t in tasks)
            {
                textBox1 += (t.Result + Environment.NewLine + Environment.NewLine);
            }

        }

        public async Task<string> DoSomething(string arg)
        {
            return arg;
        }

        public string textBox1;
    }
}

Upvotes: 5

Orifjon
Orifjon

Reputation: 1097

you can use async await

public async List<String> SomeMethod() {
   List<String> Arguments = new List<String> { "AA", "BB", "CC" }; 
   List<String> ResultList = new List<String>();  
   foreach(var argument in Arguments) 
   {
     var result = await DoSomething(argument);
     ResultList.Add(result);
   }
   return ResultList;
}

Upvotes: 0

Sami Kuhmonen
Sami Kuhmonen

Reputation: 31203

You do not need the Wait() call. Documentation for Task<T>.Result states:

Accessing the property's get accessor blocks the calling thread until the asynchronous operation is complete; it is equivalent to calling the Wait method.

Upvotes: 1

Related Questions