并行执行带参数的方法
Executing a method with parameters in parallel
我是异步和并行编程的新手,我的问题围绕着尝试 运行 一种采用几个参数的方法,然后并行执行该方法。我需要并行 运行 因为被调用的方法是更新我工厂车间的 PLC。如果同步关闭,这个过程可能需要将近 10 分钟,因为有多少。我能够使用我自定义 class 中的最后一个 PLC 获得 运行 一次方法,但不会 运行 用于我列表中的其他 PLC。示例代码如下:
List<Task> task = new List<Task>();
foreach(PLC plc in PlcCollection)
{
string plcName = plc.Name;
string tagFormat = plc.TagFormat
tasks.Add(Task.Run(async () => await MakeTags(plcName, tagFormat)));
}
Parallel.ForEach(tasks, task => task.Start());
// Code to be done after tasks are complete
public async Task MakeTags(string plcName, string tagFormat)
{
//Code to update the PLC's
}
方法 makeTags 在被调用时的工作方式与它应有的一样 - 它正确地更新了 PLC。但它只有 运行 一次,而且只针对我的一个 PLC 值。我也试过 Task.WhenAll(tasks)
而不是 Parallel.ForEach
,但同样的问题。有没有人对我做错了什么以及我可以尝试的任何事情有任何建议和反馈?
谢谢!
编辑:澄清一下——我希望 MakeTags 方法可以并行执行,无论 PlcCollection 中有多少 PLC(数量是可变的)。我得到的是当集合中有多个 PLC 时,MakeTags 方法只被调用一次,对于一个 PLC 元素。这个过程是从一个网络应用程序开始的,然后服务器会处理剩下的事情。没有 UI 个元素正在更新或更改。都是幕后工作。
编辑 2:请参阅下面的调试器屏幕截图。在这个例子中,2 个 PLC(TTC_WALL 和 RR_HEPA_EE)被加载到我的任务列表中。但是,只有 RR_HEPA_EE 被处理,然后程序完成而没有抛出异常。
编辑 3:我进行了更改,现在代码如下所示:
List<Task> task = new List<Task>();
foreach(PLC plc in PlcCollection)
{
//plcName and tagFormat are just strings
tasks.Add(MakeTags(plcName, tagFormat);
}
Task.WhenAll(tasks).ContinueWith(t =>
{
//Code to do
Console.WriteLine("****PLC Update Complete*****");
});
return; //program ends
public async Task MakeTags(string plcName, string tagFormat)
{
await Task.Run(() =>
{
Console.WriteLine("Updating PLC " + plcName);
//Code to update the PLC's
Console.WriteLine("PLC Updated for :" + plcName);
});
return;
}
然而,我仍然只得到 运行 的其中一项任务。下面的调试器屏幕截图显示 2 个 PLC 中只有 1 个已更新。我也应该看到一条控制台行,上面写着“PLC Updated for TTC_WALL”。
问题可能是您的代码没有等待任务完成。您可以使用阻塞 Task.WaitAll
方法等待它们全部完成。
Task[] tasks = PlcCollection
.Select(plc => MakeTagsAsync(plc.Name, plc.TagFormat))
.ToArray();
Task.WaitAll(tasks);
Console.WriteLine("****PLC Update Complete*****");
这样,当前线程将被阻塞,直到所有任务完成,并且可能发生的任何异常都将捆绑在 AggregateException
.
中浮出水面。
上述方法假定 MakeTagsAsync
方法是真正异步的。如果不是,而是通过在内部包装 Task.Run
中的一些同步代码来伪造异步,首先您应该在此处阅读为什么这是一个坏主意:Should I expose asynchronous wrappers for synchronous methods? Then make the method synchronous again by removing the Task.Run
wrapper and changing the return type to void
, and use the Parallel
class or the PLINQ library to invoke the method in parallel. Here is a PLINQ 示例:
PlcCollection
.AsParallel()
.ForAll(plc => MakeTags(plc.Name, plc.TagFormat));
Console.WriteLine("****PLC Update Complete*****");
我是异步和并行编程的新手,我的问题围绕着尝试 运行 一种采用几个参数的方法,然后并行执行该方法。我需要并行 运行 因为被调用的方法是更新我工厂车间的 PLC。如果同步关闭,这个过程可能需要将近 10 分钟,因为有多少。我能够使用我自定义 class 中的最后一个 PLC 获得 运行 一次方法,但不会 运行 用于我列表中的其他 PLC。示例代码如下:
List<Task> task = new List<Task>();
foreach(PLC plc in PlcCollection)
{
string plcName = plc.Name;
string tagFormat = plc.TagFormat
tasks.Add(Task.Run(async () => await MakeTags(plcName, tagFormat)));
}
Parallel.ForEach(tasks, task => task.Start());
// Code to be done after tasks are complete
public async Task MakeTags(string plcName, string tagFormat)
{
//Code to update the PLC's
}
方法 makeTags 在被调用时的工作方式与它应有的一样 - 它正确地更新了 PLC。但它只有 运行 一次,而且只针对我的一个 PLC 值。我也试过 Task.WhenAll(tasks)
而不是 Parallel.ForEach
,但同样的问题。有没有人对我做错了什么以及我可以尝试的任何事情有任何建议和反馈?
谢谢!
编辑:澄清一下——我希望 MakeTags 方法可以并行执行,无论 PlcCollection 中有多少 PLC(数量是可变的)。我得到的是当集合中有多个 PLC 时,MakeTags 方法只被调用一次,对于一个 PLC 元素。这个过程是从一个网络应用程序开始的,然后服务器会处理剩下的事情。没有 UI 个元素正在更新或更改。都是幕后工作。
编辑 2:请参阅下面的调试器屏幕截图。在这个例子中,2 个 PLC(TTC_WALL 和 RR_HEPA_EE)被加载到我的任务列表中。但是,只有 RR_HEPA_EE 被处理,然后程序完成而没有抛出异常。
编辑 3:我进行了更改,现在代码如下所示:
List<Task> task = new List<Task>();
foreach(PLC plc in PlcCollection)
{
//plcName and tagFormat are just strings
tasks.Add(MakeTags(plcName, tagFormat);
}
Task.WhenAll(tasks).ContinueWith(t =>
{
//Code to do
Console.WriteLine("****PLC Update Complete*****");
});
return; //program ends
public async Task MakeTags(string plcName, string tagFormat)
{
await Task.Run(() =>
{
Console.WriteLine("Updating PLC " + plcName);
//Code to update the PLC's
Console.WriteLine("PLC Updated for :" + plcName);
});
return;
}
然而,我仍然只得到 运行 的其中一项任务。下面的调试器屏幕截图显示 2 个 PLC 中只有 1 个已更新。我也应该看到一条控制台行,上面写着“PLC Updated for TTC_WALL”。
问题可能是您的代码没有等待任务完成。您可以使用阻塞 Task.WaitAll
方法等待它们全部完成。
Task[] tasks = PlcCollection
.Select(plc => MakeTagsAsync(plc.Name, plc.TagFormat))
.ToArray();
Task.WaitAll(tasks);
Console.WriteLine("****PLC Update Complete*****");
这样,当前线程将被阻塞,直到所有任务完成,并且可能发生的任何异常都将捆绑在 AggregateException
.
上述方法假定 MakeTagsAsync
方法是真正异步的。如果不是,而是通过在内部包装 Task.Run
中的一些同步代码来伪造异步,首先您应该在此处阅读为什么这是一个坏主意:Should I expose asynchronous wrappers for synchronous methods? Then make the method synchronous again by removing the Task.Run
wrapper and changing the return type to void
, and use the Parallel
class or the PLINQ library to invoke the method in parallel. Here is a PLINQ 示例:
PlcCollection
.AsParallel()
.ForAll(plc => MakeTags(plc.Name, plc.TagFormat));
Console.WriteLine("****PLC Update Complete*****");