I'm using Parallel.Invoke
to run certain methods simultaneously and collect the results when all methods are finished.
PROBLEM
As you can see on the "horrible code" section, the list of actions is hardcoded to three elements, and will be totally useless if the detectedDevicesList.Count != 3
.
TRIED SOLUTIONS
I tried to dynamically create an Actions[]
array and pass it as an argument for Parallel.Invoke
, but I'm unable to convert my existing methods into Task
s, and then into Action
s.
NON WORKING CODE
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;
});
}
EDIT: USEFUL INFORMATION FOR THE READER AND LESSONS LEARNED
Following the excellent answers and comments received, I added some originally missing code that may help you to safely interact with Windows Form objects from parallel tasks.
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;
}
}
Also, refrain from blindly following IntelliSense suggestions, to prevent shenanigans like Task<Task>, or using Java-like casing on C#.
Is hard to pick the best-proposed answer, because all of them work correctly and without any visible performance difference (the MakePhoneCall().call makes automated phone calls with Android devices connected via ADB). Check which answer works best for your specific application.