Reputation: 767
How do I go about switching between async/sync processing of multiple tasks in C#?
I'd like to be able to switch between parallel and synchronous processing of tasks for testing/profiling purposes.
Initially I had an array of Tasks initialised inline. While this was fine for parallel, I found my synchronous code wouldn't work because each method would be activated on initialization of the array.
I decided to try Lazy instead. Is this code correct? If not, how is this best achieved?
var runTasks = new Lazy<Task<ProcessWithResult>>[]
{
new Lazy<Task<ProcessWithResult>>(() => GetHeaderRecord()),
new Lazy<Task<ProcessWithResult>>(() => GetChildType1Entities()),
new Lazy<Task<ProcessWithResult>>(() => GetChildType2Entities()),
new Lazy<Task<ProcessWithResult>>(() => GetChildType3Entities()),
};
if (Settings.Default.AsyncProcessing)
{
Task.WaitAll(runTasks.Select(x => x.Value).ToArray());
}
else
{
// do these in sequence
runTasks.ToList().ForEach(t => t.Value.Wait());
}
// carryon...
Each GetX() method signature is like so:
public async Task<ProcessWithResult> GetChildType1Entities()
With at least one async operation on Database or file i/o.
Upvotes: 3
Views: 990
Reputation: 7681
Your approach could work, however i would avoid using Lazy for this.
There is a lazy language construct in C# already, it's yield return, and it would result in cleaner code:
public static class Runner
{
public static async Task<IEnumerable<ProcessWithResult>> RunAll(this IEnumerable<Task<ProcessWithResult>> tasks, bool runSequentially)
{
if (!runSequentially) return await Task.WhenAll(tasks);
var results = new List<ProcessWithResult>();
foreach (var task in tasks)
results.Add(await task);
return results;
}
}
public class Tests
{
[Test]
public void RunInParallel()
{
var results = GetAllTasks().RunAll(false).Result;
CollectionAssert.AreEqual(new[] { 2, 1 }, results.OrderBy(r => r.WhenFinished).Select(r => r.Id));
}
[Test]
public void RunInSequentially()
{
var results = GetAllTasks().RunAll(true).Result;
CollectionAssert.AreEqual(new[] { 1, 2 }, results.OrderBy(r => r.WhenFinished).Select(r => r.Id));
}
IEnumerable<Task<ProcessWithResult>> GetAllTasks()
{
yield return RunTask(1, 1000);
yield return RunTask(2, 100);
}
async Task<ProcessWithResult> RunTask(int id, int wait)
{
await Task.Delay(wait);
return new ProcessWithResult {Id = id, WhenFinished = DateTime.Now};
}
}
public class ProcessWithResult
{
public int Id { get; set; }
public DateTime WhenFinished { get; set; }
}
This way the method still runs asyncronously both in production and the tests.
Upvotes: 2