执行带有参数的动态动作列表
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.Add
List.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
来索引 List
或 array
它总是会导致 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); });
}
}
我正在根据其他一些数据构建一个操作列表。每个动作都应该调用一个方法,并且动作列表应该并行执行。我有以下代码,适用于无参数方法:
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.Add
List.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
来索引 List
或 array
它总是会导致 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); });
}
}