user183872
user183872

Reputation: 767

How to switch between async & sync in .NET/C#

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

Answers (1)

George Polevoy
George Polevoy

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

Related Questions