我根据其他一些数据构建了一系列动作。每个操作都应该调用方法,并且应该并行执行操作列表。我有以下代码适用于无参数方法:
private void Execute() {
List<Action> actions = new List<Action>();
for (int i = 0; i < 5; i++) {
actions.Add(new Action(DoSomething));
}
Parallel.Invoke(actions.ToArray());
}
private void DoSomething() {
Console.WriteLine("Did something");
}
但是如何在方法有参数的地方做类似的事情呢?以下不起作用:
private void Execute() {
List<Action<int>> actions = new List<Action<int>>();
for (int i = 0; i < 5; i++) {
actions.Add(new Action(DoSomething(i))); // This fails because I can't input the value to the action like this
}
Parallel.Invoke(actions.ToArray()); // This also fails because Invoke() expects Action[], not Action<T>[]
}
private void DoSomething(int value) {
Console.WriteLine("Did something #" + value);
}
答案 0 :(得分:6)
只需将actions
变量保留为List<Action>
而不是List<Action<int>>
,并解决这两个问题:
private void Execute() {
List<Action> actions = new List<Action>();
for (int i = 0; i < 5; i++) {
actions.Add(new Action(() => DoSomething(i))); }
Parallel.Invoke(actions.ToArray());
}
private void DoSomething(int value) {
Console.WriteLine("Did something #" + value);
}
您想要Action
的原因是因为您在调用操作时未传递参数 - 您提供参数值作为部分委托定义(注意委托参数更改为没有输入参数的lambda - () => DoSomething(i)
),因此它是Action
,而不是Action<int>
}。
答案 1 :(得分:3)
在使用索引的forloop创建线程时遇到了一个陷阱。
将参数i
直接提供给DoSomething
方法时,输出为:
做了一些事情#5
做了一些事情#5
做了一些事情#5
做了一些事情#5
做了一些事情#5
当使用循环并更改计数变量时,可能不是人们想要的。但是如果你将它暂时保存到一个新的变量中,如:
class Program
{
private static void Execute()
{
List<Action> actions = new List<Action>();
for (int i = 0; i < 5; i++)
{
// save it temporarily and pass the temp_var variable
int tmp_var = i;
actions.Add(new Action(() => DoSomething(tmp_var)));
}
Parallel.Invoke(actions.ToArray());
}
private static void DoSomething(int value)
{
Console.WriteLine("Did something #" + value);
}
static void Main(string[] args)
{
Execute();
Console.ReadKey();
}
}
你实际上得到了完整的美丽计数变量:
做了什么#0
做了什么#2
做了一些事情#3
做了一些事情#1
做了#4
显然在C#中,变量在循环中存活(我不知道在哪里),当线程执行时,编译器将跳转到该行
actions.Add(new Action(() => DoSomething(i)));
并获取Loop结束时的i
值!如果您使用i
为List
或array
编制索引,则始终会导致OutOfBoundsException
!
这让我生气了一个星期,直到我弄清楚
答案 2 :(得分:0)
作为j.i.h.指出,你需要在Lamba中使用它。将您的代码段更改为:
private void Execute()
{
List<Action> actions = new List<Action>();
for (int i = 0; i < 5; i++)
{
actions.Add(() => DoSomething(i));
}
Parallel.Invoke(actions.ToArray());
}
private void DoSomething(int value)
{
Console.WriteLine("Did something #" + value);
}
您可以省略new Action(() => DoSomething(i));
并直接将Lamba传递给List.Add
List.Add(() => DoSomething(i));
答案 3 :(得分:0)
看起来你真正想要的只是:
private void Execute() {
Parallel.For(0, 5, DoSomething);
}
private void DoSomething(int value) {
Console.WriteLine("Did something #" + value);
}
答案 4 :(得分:0)
Mong Jhu的答案很完美,我想说明Action<int>
如何用于相同的输出。
private static void Execute1()
{
List<Action> actions = new List<Action>();
for (int i = 0; i < 5; i++)
{
actions.Add(new Action(() => DoSomething(i)));
}
Parallel.Invoke(actions.ToArray());
}
private static void Execute()
{
List<Action<int>> actions = new List<Action<int>>();
for (int i = 0; i < 5; i++)
{
actions.Add(new Action<int>((x) => DoSomething(i)));
}
for (int i = 0; i < actions.Count; i++)
{
Parallel.Invoke(() => { actions[0](0); });
}
}