C# 并行执行带参数的方法
我不熟悉异步和并行编程,我的问题围绕着尝试运行一个包含两个参数的方法,然后并行执行该方法。我需要并行运行此程序,因为调用的方法正在更新我工厂车间的PLC。如果它是同步关闭,这个过程可能需要近10分钟,因为有多少。我可以使用自定义类中的最后一个PLC使该方法运行一次,但不会为列表中的其他PLC运行。示例代码如下:C# 并行执行带参数的方法,c#,task,task-parallel-library,C#,Task,Task Parallel Library,我不熟悉异步和并行编程,我的问题围绕着尝试运行一个包含两个参数的方法,然后并行执行该方法。我需要并行运行此程序,因为调用的方法正在更新我工厂车间的PLC。如果它是同步关闭,这个过程可能需要近10分钟,因为有多少。我可以使用自定义类中的最后一个PLC使该方法运行一次,但不会为列表中的其他PLC运行。示例代码如下: List<Task> task = new List<Task>(); foreach(PLC plc in PlcCollection) { strin
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
}
但是,我仍然只能运行其中一个任务。调试器屏幕截图如下,仅显示2个PLC中的1个已更新。我应该看到一个控制台行说“PLC更新为TTC_墙”。
问题可能是您的代码没有等待任务完成。您可以使用阻塞方法等待它们全部完成 这样,当前线程将被阻塞,直到所有任务完成,并且可能发生的任何异常都将在
aggregateeexception
中捆绑显示
上述方法假设MakeTagsAsync
方法是真正异步的。如果不是,而是通过在Task.Run
中内部包装一些同步代码来假装异步。首先,您应该了解为什么这是一个坏主意:然后通过删除Task.Run
包装并将返回类型更改为void
,使方法再次同步,并使用Parallel
类或PLINQ库并行调用该方法。以下是一个例子:
您想一次调用所有PLC的
MakeTags
,还是想?为什么要使用Task.Run()
?此调用将线程池上的工作线程排队,这是不必要的,因为MakeTags()
是异步的,可能是IO绑定的。您只需将MakeTags(plcName,tagFormat)
添加到Task
列表中,然后使用Task.WhenAll()等待结果。此外,您不需要使用Parallel.ForEach
来“开始”工作。调用Task.Run()
时,一个线程将排入线程池队列,并将在任务计划程序允许的情况下运行。在调用异步方法的情况下,它们在被调用时开始。@PatrickTucci我正在使用Task.Run(),因为这是我对如何设置任务的理解-这是我第一次尝试异步编程,学习起来很有挑战性。如果我理解你的意思,我应该尝试以下操作:tasks.Add(MakeTags(plcName,tagFormat)
然后Task.whalll(tasks.ToArray())
?@JohnMuraski一个任务
只代表一个异步操作。它不是线程或并行的简写。区别很重要,你应该研究,但不在评论讨论的范围内。要回答你的问题,是的,你发布的代码应该做你想做的。值得注意的是,如果MakeTags()
包含CPU密集型代码,这可能会给服务器带来很大的负载。如果它是IO绑定的,这应该不是问题。问题很可能出在MakeTags
中。谢谢Theodor Zoulias和@Patrick Tucci!我在上面的答案中使用了PLINQ方法,它起到了作用。我的方法开始并行运行!仍然有很多问题需要解决学习并行编程,但这是一个很好的开始。@JohnMuraski如果您想控制并行度,PLINQ包含一个带degreeofparallelism
操作符,您可以在AsParallel
操作符之后附加该操作符。请注意,并行性还受到线程池可用性的进一步限制ode>threads。当ThreadPool
饱和时,它会以较慢的速度注入新线程。因此,请记住在程序开始时使用ThreadPool.SetMinThreads
方法的选项,以配置(增加)ThreadPool
根据需要立即创建的最小线程数。
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;
}
Task[] tasks = PlcCollection
.Select(plc => MakeTagsAsync(plc.Name, plc.TagFormat))
.ToArray();
Task.WaitAll(tasks);
Console.WriteLine("****PLC Update Complete*****");
PlcCollection
.AsParallel()
.ForAll(plc => MakeTags(plc.Name, plc.TagFormat));
Console.WriteLine("****PLC Update Complete*****");