运行 多个任务(可变数量)并行并在所有任务完成后继续

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

我需要启动 "number" 个非并行任务(可变但少于 10 个), 并等待他们全部完成,从每个人那里得到结果。 我从他们每个人那里得到结果,保存在一个列表中,然后在最后使用它。

这是我的代码,它可以正常工作,但我认为必须有一种更简洁的方法来做到这一点。

导致任务数量

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);
}

您不需要 Wait() 调用。 Task<T>.Result 的文档说明:

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

你可以使用异步等待

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;
}

我认为这就是您正在尝试做的事情:即启动一整套并行任务并等待它们全部完成后再继续

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;
    }
}

到目前为止,您的整个代码都是同步运行的。您正在创建一个 Task 然后主动阻止它,使用 Task.Result.

如果您真正理解什么是并行性,并且您确实需要执行与列表中的参数一样多的任务,那么您可以卸载所有任务然后异步等待(这是 key,当它们完成时 异步):

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);
}

编辑: 来自您的评论:

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

那么您根本不需要为每个参数创建线程。两件事:

  1. 您可以使调用完全同步,因为您必须等待每个调用完成。为此使用线程池线程毫无用处
  2. 您可以在根本不需要使用线程的情况下进行异步调用。查看 HttpClient 及其 XXXAsync 方法(例如 GetAsync)。