执行带有参数的动态动作列表

Executing dynamic list of Actions with parameters

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

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

只需将 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>.

作为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.AddList.Add(() => DoSomething(i));

在创建线程时使用索引 forloop 时,我 运行 陷入了一个陷阱。

将参数 i 直接提供给 DoSomething 方法时,输出为:

Did something #5

Did something #5

Did something #5

Did something #5

Did something #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();
    }
}

你实际上得到了完整美丽的计数变量:

Did something #0

Did something #2

Did something #3

Did something #1

Did something #4

显然在 C# 中,变量在循环中存活(我不知道在哪里),当线程执行时,编译器将跳转到行

actions.Add(new Action(() => DoSomething(i)));

并取 i 循环结束时的值!如果您使用 i 来索引 Listarray 它总是会导致 OutOfBoundsException ! 这让我疯狂了一个星期,直到我弄明白

看来你真正想要的只是:

private void Execute() {
    Parallel.For(0, 5, DoSomething);
}

private void DoSomething(int value) {
    Console.WriteLine("Did something #" + value);
}

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