I am developing an application that will read excel files from disk and then process tests based on the data in the files. In order to keep the user interface from locking up when loading files and running tests, I have implemented the async/await with Task.Run() to handle the loads and tests running. Right now I only have one test and three input files, but I want to make sure that this application will scale as the number of files & tests increase. My other concerns are not taking up too much processing power by having too many threads and being able to report the progress of the file loads and tests. Have I correctly implemented async/await/Task.Run in order to satisfy these concerns?
private async void ProcessTestsAsync()
{
try
{
//TestFactory testFact = new TestFactory();
if (WorkingDirectory != null && (from f in Files where f.Location == "" select f).FirstOrDefault() == null)
{
// Load Files
var loadResults = await LoadFilesAsync();
// Run Tests
if (loadResults)
{
var testResults = await RunTestsAsync();
if (testResults)
{
MessageBox.Show("Success");
}
else
{
MessageBox.Show("Failure");
}
}
else
{
MessageBox.Show("File Load Failed. Please check your files and try again.");
}
}
else
{
MessageBox.Show("One or more files has not been selected. Please choose any missing files and try again.");
}
}
catch (Exception err)
{
MessageBox.Show("Tests failed. Please try again. Error: " + err.Message);
}
}
private async Task<bool> RunTestsAsync()
{
try
{
using (var sem = new SemaphoreSlim(MAX_THREADS)) // Limit the number of threads that can run at a time
{
TestFactory testFact = new TestFactory();
var tasks = new List<Task>();
foreach (Model.Test test in IncludedTests)
{
await sem.WaitAsync();
tasks.Add(Task.Run(() =>
{
SortedList<Enums.FileType, DataTable> sources = new SortedList<Enums.FileType, DataTable>();
foreach (Enums.FileType fileType in test.ExpectedSources)
{
sources.Add(fileType, _files[fileType]);
}
test.Progress = 25;
TestBase t = testFact.getTest(test.Type);
if (t.SetWorkingDirectory(WorkingDirectory))
{
test.Progress = 50;
if (t.SetSources(sources))
{
test.Progress = 75;
if (t.RunTest())
{
test.Progress = 100;
}
}
}
else
{
MessageBox.Show("Test Failed.");
}
}));
}
await Task.WhenAll(tasks);
}
return true;
}
catch (Exception)
{
return false;
}
}
private async Task<bool> LoadFilesAsync()
{
try
{
_files.Clear();
using (var sem = new SemaphoreSlim(MAX_THREADS))
{
var tasks = new List<Task>();
foreach (var file in Files)
{
await sem.WaitAsync(); // Limit the number of threads that can run at a time
tasks.Add(Task.Run(() =>
{
file.FileLoadStatus = Enums.FileLoadStatus.InProgress;
_files.Add(file.Type, file.Source.LoadRecords(file));
file.FileLoadStatus = Enums.FileLoadStatus.Completed;
}));
}
await Task.WhenAll(tasks);
}
return true;
}
catch (Exception)
{
return false;
}
}
Best Answer
At first glance, I would say the
async
/await
code looks OK. However, there are a few things to consider.LoadFilesAsync()
LoadFilesAsync()
).MessageBox.ShowMessage()
isn't a good idea deep in your tasks (example inRunTestsAsync()
when the handling class also does the same thing)In your
LoadFilesAsync()
method I would restructure it like this: