使用参数执行Actions的动态列表

时间:2016-06-17 13:31:52

标签: c# action

我根据其他一些数据构建了一系列动作。每个操作都应该调用方法,并且应该并行执行操作列表。我有以下代码适用于无参数方法:

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

5 个答案:

答案 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值!如果您使用iListarray编制索引,则始终会导致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); });
            }
        }