首页 > 解决方案 > 并行执行带参数的方法

问题描述

我是异步和并行编程的新手,我的问题围绕着尝试运行一个采用几个参数的方法,然后并行执行该方法。我需要并行运行它,因为被调用的方法是更新我工厂车间的 PLC。如果它同步关闭,这个过程可能需要将近 10 分钟,因为有多少。我可以使用自定义类中的最后一个 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(数量将是可变的)。我得到的是 MakeTags 方法只被调用一次,对于一个 PLC 元素,当集合中有多个 PLC 时。此过程从 Web 应用程序开始,然后服务器负责其余的工作。没有 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 个已更新。我应该会看到一条控制台行,上面写着“为 TTC_WALL 更新了 PLC”。 在此处输入图像描述

标签: c#tasktask-parallel-library

解决方案


问题可能是您的代码没有等待任务完成。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,首先您应该在这里阅读为什么这是一个坏主意:我应该为同步方法公开异步包装器吗?Task.Run然后通过删除包装器并将返回类型更改为再次使方法同步void,并使用Parallel类或 PLINQ 库并行调用该方法。这是一个PLINQ示例:

PlcCollection
    .AsParallel()
    .ForAll(plc => MakeTags(plc.Name, plc.TagFormat));
Console.WriteLine("****PLC Update Complete*****");

推荐阅读