【发布时间】:2021-10-23 00:39:57
【问题描述】:
我正在使用Parallel.Invoke 同时运行某些方法,并在所有方法完成后收集结果。
问题
正如您在“可怕的代码”部分看到的,动作列表被硬编码为三个元素,如果detectedDevicesList.Count != 3 将完全无用。
尝试过的解决方案
我尝试动态创建一个Actions[] 数组并将其作为Parallel.Invoke 的参数传递,但我无法将现有方法转换为Tasks,然后再转换为Actions。
非工作代码
public async Task<Task<String>> callWithEveryConnectedDevice(ListBox listBoxLog, Boolean sendAlarms)
{
String TEST_CALLS_COMPLETED = "All test calls completed.";
String TEST_CALLS_FAILED = "One or more test cals failed";
return Task.Run(async () =>
{
List<MobileEquipment> detectedDevicesList = await GetConnectedDevices.getAsync();
if (detectedDevicesList.Count == 0)
{
UpdateGui.listboxAddItem(listBoxLog, "No devices are connected.", true);
return TEST_CALLS_FAILED;
}
Console.WriteLine("Executing test calls...");
List<Task<MobileEquipment>> results = new List<Task<MobileEquipment>>();
//Horrible code begins...
Parallel.Invoke(() =>
{
results.Add(new MakePhoneCall().call(detectedDevicesList[0], listBoxLog));
},
() =>
{
results.Add(new MakePhoneCall().call(detectedDevicesList[1], listBoxLog));
},
() =>
{
results.Add(new MakePhoneCall().call(detectedDevicesList[2], listBoxLog));
});
//Horrible code ends...
foreach (Task<MobileEquipment> mobileEquipment in results)
{
UpdateGui.listboxAddItem(listBoxLog, "Test call result for " + mobileEquipment.Result.serial + " " + mobileEquipment.Result.operador + ": " + mobileEquipment.Result.callSuccess, true);
if (!mobileEquipment.Result.callSuccess && sendAlarms)
{
await SendEmail.sendAlarmEmailsAsync(libreta, asunto, mensaje);
}
}
UpdateGui.listboxAddItem(listBoxLog, TEST_CALLS_COMPLETED, true);
return TEST_CALLS_COMPLETED;
});
}
编辑:给读者的有用信息和学到的经验
根据收到的出色答案和 cmets,我添加了一些原本缺失的代码,这些代码可以帮助您从并行任务中安全地与 Windows 窗体对象进行交互。
public static void ListboxAddItem(ListBox listBox, String argText, Boolean useTimestamp)
{
String timeStamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
if (useTimestamp)
{
argText = timeStamp + ": " + argText;
}
if (Thread.CurrentThread.IsBackground)
{
listBox.Invoke(new Action(() =>
{
listBox.Items.Add(argText);
listBox.SelectedIndex = listBox.Items.Count - 1;
}));
}
else
{
listBox.Items.Add(argText);
listBox.SelectedIndex = listBox.Items.Count - 1;
}
}
此外,不要盲目遵循 IntelliSense 建议,以防止像 Task
很难选择最佳建议的答案,因为它们都可以正常工作并且没有任何明显的性能差异(MakePhoneCall().call 通过 ADB 连接的 Android 设备自动拨打电话)。检查哪个答案最适合您的特定应用。
【问题讨论】:
-
也许使用
Parallel.For?但要小心并行结果。添加。我怀疑 List 可以处理并行添加。为了安全起见,请使用并发 docs.microsoft.com/de-de/dotnet/api/… -
每当您必须编写这样的返回类型时:
Task<Task<String>>这是一个好兆头,表明您需要修改您的解决方案。 -
MakePhoneCall().call方法是否受 I/O 或 CPU 限制?Parallel.XYZ专为 CPU 绑定操作而设计。 -
驼峰式方法名称在 C# 中不是标准的。帕斯卡大小写是常态。
-
MakePhoneCall.call()是做什么的?如果它使任何类型的 IOParallel.Invoke是 wrong 方法。事实上,即使是 CPU 密集型代码,也有更好的选择,例如使用 ActionBlock 或 Channel
标签: c# parallel-processing task-parallel-library parallel.invoke