如何一次使用 Task.WhenAll 到 运行 2 次计算
How to use Task.WhenAll to run 2 calculations at once
我正在努力学习如何使用异步任务,但我真的很努力只是为了让一个简单的例子起作用。我在下面提供了我要实现的目标的非常简化的版本。
public void Calculate()
{
int a1 = 1;
int a2 = 2;
int b1 = 3;
int b2 = 4;
int c3 = 5;
int c4 = 6;
int c5 = 7;
int c6 = 8;
int c7 = 9;
// Do next 2 lines in parallel
int rank1 = Evaluate(a1, a2, c3, c4, c5, c6, c7);
int rank2 = Evaluate(b1, b2, c3, c4, c5, c6, c7);
// wait for above 2 lines and use values to get result
int result = EvaluateOutcome(rank1, rank2);
}
public static int Evaluate(int i1, int i2, int i3, int i4, int i5, int i6, int i7)
{
// do something complicated and return value - simulate here with random number
Random rand = new Random();
return rand.Next(0,10);
}
public static int EvaluateOutcome(int rank1, int rank2)
{
return rank1 * rank2;
}
计算rank1
和rank2
在我的真实代码中是一个漫长的过程,但计算是相互独立的。我想尝试 运行 一次计算这两个计算,希望处理时间减半。在计算结果之前,我需要等待两个计算完成。
我想我应该可以做类似的事情:
private async Task Evaluate(int a1, int a2, int b1, int b2, int c3, int c4, int c5, int c6, int c7)
{
Task task1 = Evaluate(a1, a2, c3, c4, c5, c6, c7);
Task task2 = Evaluate(b1, b2, c3, c4, c5, c6, c7);
await Task.WhenAll(task1, task2);
}
但这无法编译,我不确定如何从任务中获取结果并将它们放入我的 rank1
和 rank2
int 变量中。我认为这应该非常简单,但我找不到一个明确的例子来遵循。一些帮助将不胜感激。
您可以使用Task.Run
method to offload each calculation to a ThreadPool
thread, and the Task.WaitAll
方法来阻塞当前线程,直到两个计算完成:
Task<int> task1 = Task.Run(() => Evaluate(a1, a2, c3, c4, c5, c6, c7));
Task<int> task2 = Task.Run(() => Evaluate(b1, b2, c3, c4, c5, c6, c7));
Task.WaitAll(task1, task2);
int rank1 = task1.Result;
int rank2 = task2.Result;
更有效的替代方法是使用 Parallel.Invoke
方法。核心区别在于当前线程也将通过执行两个计算之一参与工作。所以只会使用一个额外的线程(来自 ThreadPool
):
int rank1 = default;
int rank2 = default;
var parallelOptions = new ParallelOptions()
{
MaxDegreeOfParallelism = Environment.ProcessorCount
};
Parallel.Invoke(parallelOptions,
() => rank1 = Evaluate(a1, a2, c3, c4, c5, c6, c7),
() => rank2 = Evaluate(b1, b2, c3, c4, c5, c6, c7)
);
Parallel.Invoke
的文档有点令人困惑,声明它 执行每个提供的操作,可能 并行。不用担心。计算不会并行发生的唯一情况是 ThreadPool
饱和。在这种情况下,第一种方法 (Task.Run
+Task.WaitAll
) 也无法并行计算,同时对池的饱和度贡献更大。如果你想把 ThreadPool
从等式中去掉,你可以传递一个自定义的 TaskScheduler
with the ParallelOptions
, that executes the tasks on a dedicated thread per task, like the one found here. Or use the TaskCreationOptions.LongRunning
标志。
我正在努力学习如何使用异步任务,但我真的很努力只是为了让一个简单的例子起作用。我在下面提供了我要实现的目标的非常简化的版本。
public void Calculate()
{
int a1 = 1;
int a2 = 2;
int b1 = 3;
int b2 = 4;
int c3 = 5;
int c4 = 6;
int c5 = 7;
int c6 = 8;
int c7 = 9;
// Do next 2 lines in parallel
int rank1 = Evaluate(a1, a2, c3, c4, c5, c6, c7);
int rank2 = Evaluate(b1, b2, c3, c4, c5, c6, c7);
// wait for above 2 lines and use values to get result
int result = EvaluateOutcome(rank1, rank2);
}
public static int Evaluate(int i1, int i2, int i3, int i4, int i5, int i6, int i7)
{
// do something complicated and return value - simulate here with random number
Random rand = new Random();
return rand.Next(0,10);
}
public static int EvaluateOutcome(int rank1, int rank2)
{
return rank1 * rank2;
}
计算rank1
和rank2
在我的真实代码中是一个漫长的过程,但计算是相互独立的。我想尝试 运行 一次计算这两个计算,希望处理时间减半。在计算结果之前,我需要等待两个计算完成。
我想我应该可以做类似的事情:
private async Task Evaluate(int a1, int a2, int b1, int b2, int c3, int c4, int c5, int c6, int c7)
{
Task task1 = Evaluate(a1, a2, c3, c4, c5, c6, c7);
Task task2 = Evaluate(b1, b2, c3, c4, c5, c6, c7);
await Task.WhenAll(task1, task2);
}
但这无法编译,我不确定如何从任务中获取结果并将它们放入我的 rank1
和 rank2
int 变量中。我认为这应该非常简单,但我找不到一个明确的例子来遵循。一些帮助将不胜感激。
您可以使用Task.Run
method to offload each calculation to a ThreadPool
thread, and the Task.WaitAll
方法来阻塞当前线程,直到两个计算完成:
Task<int> task1 = Task.Run(() => Evaluate(a1, a2, c3, c4, c5, c6, c7));
Task<int> task2 = Task.Run(() => Evaluate(b1, b2, c3, c4, c5, c6, c7));
Task.WaitAll(task1, task2);
int rank1 = task1.Result;
int rank2 = task2.Result;
更有效的替代方法是使用 Parallel.Invoke
方法。核心区别在于当前线程也将通过执行两个计算之一参与工作。所以只会使用一个额外的线程(来自 ThreadPool
):
int rank1 = default;
int rank2 = default;
var parallelOptions = new ParallelOptions()
{
MaxDegreeOfParallelism = Environment.ProcessorCount
};
Parallel.Invoke(parallelOptions,
() => rank1 = Evaluate(a1, a2, c3, c4, c5, c6, c7),
() => rank2 = Evaluate(b1, b2, c3, c4, c5, c6, c7)
);
Parallel.Invoke
的文档有点令人困惑,声明它 执行每个提供的操作,可能 并行。不用担心。计算不会并行发生的唯一情况是 ThreadPool
饱和。在这种情况下,第一种方法 (Task.Run
+Task.WaitAll
) 也无法并行计算,同时对池的饱和度贡献更大。如果你想把 ThreadPool
从等式中去掉,你可以传递一个自定义的 TaskScheduler
with the ParallelOptions
, that executes the tasks on a dedicated thread per task, like the one found here. Or use the TaskCreationOptions.LongRunning
标志。