Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# async await Task.delay in Action

I'm having some trouble getting a task to asynchronously delay. I am writing an application that needs to run at a scale of tens/hundreds of thousands of asynchronously executing scripts. I am doing this using C# Actions and sometimes, during the execution of a particular sequence, in order for the script to execute properly, it needs to wait on an external resource to reach an expected state. At first I wrote this using Thread.Sleep() but that turned out to be a torpedo in the applications performance, so I'm looking into async/await for async sleep. But I can't get it to actually wait on the pause! Can someone explain this?

static void Main(string[] args)
    {
        var sync = new List<Action>();
        var async = new List<Action>();

        var syncStopWatch = new Stopwatch();
        sync.Add(syncStopWatch.Start);
        sync.Add(() => Thread.Sleep(1000));
        sync.Add(syncStopWatch.Stop);
        sync.Add(() => Console.Write("Sync:\t" + syncStopWatch.ElapsedMilliseconds + "\n"));

        var asyncStopWatch = new Stopwatch();
        sync.Add(asyncStopWatch.Start);
        sync.Add(async () => await Task.Delay(1000));
        sync.Add(asyncStopWatch.Stop);
        sync.Add(() => Console.Write("Async:\t" + asyncStopWatch.ElapsedMilliseconds + "\n"));

        foreach (Action a in sync)
        {
            a.Invoke();
        }

        foreach (Action a in async)
        {
            a.Invoke();
        }
    }

The results of the execution are:

Sync: 999

Async: 2

How do I get it to wait asynchronously?

like image 968
StellaTerra Avatar asked Mar 23 '23 05:03

StellaTerra


1 Answers

You're running into a problem with async void. When you pass an async lambda to an Action, the compiler is creating an async void method for you.

As a best practice, you should avoid async void.

One way to do this is to have your list of actions actually be a List<Func<Task>> instead of List<Action>. This allows you to queue async Task methods instead of async void methods.

This means your "execution" code would have to wait for each Task as it completes. Also, your synchronous methods would have to return Task.FromResult(0) or something like that so they match the Func<Task> signature.

If you want a bigger scope solution, I recommend you strongly consider TPL Dataflow instead of creating your own queue.

like image 163
Stephen Cleary Avatar answered Mar 31 '23 22:03

Stephen Cleary