Milad Bonakdar
Milad Bonakdar

Reputation: 303

How to call an async function synchronized without blocking the main thread?

I wrote a simple console application to test ConfigureAwait(false) for fixing the problem of calling an async function in a synchronized way. This is how I simulate an event and trying to call an async function in a sync way.

 public static class Test2
    {
        private static EventHandler<int> _somethingEvent;

        public static void Run()
        {
            _somethingEvent += OnSomething;
            _somethingEvent(null, 10);
            Console.WriteLine("fetch some other resources");
            Console.ReadLine();
        }

        private static void OnSomething(object sender, int e)
        {
            Console.WriteLine($"passed value : {e}");
            FakeAsync().Wait();
        }

        private static async Task FakeAsync()
        {
            await Task.Delay(2000).ConfigureAwait(false);
            Console.WriteLine($"task completed");
        }
    }

the output of the code is like this:

passed value : 10
task completed
fetch some other resources

And when I change the OnSomething function to async void like this:

private static async void OnSomething(object sender, int e)
{
    Console.WriteLine($"passed value : {e}");
    await FakeAsync();
}

The output will change to this:

passed value : 10
fetch some other resources
task completed

You can see fetch some other resources is occurred before task completed section and this is my desired result.
My question is how can I achieve the same result by calling the async function synchronized like the first signature I wrote for the OnSomething function?
How could I use ConfigureAwait(false) to help me in this situation? Or maybe I am not using it properly.

Upvotes: 3

Views: 1795

Answers (2)

Stephen Cleary
Stephen Cleary

Reputation: 456322

I wrote a simple console application to test ConfigureAwait(false) for fixing the problem of calling an async function in a synchronized way.

There's a couple of problems with this.

  1. ConfigureAwait(false) is not a "fix" for the "problem" of calling an asynchronous function synchronously. It is one possible hack that works in some situations, but it's not generally recommended because you have to use ConfigureAwait(false) everywhere in the entire transitive closure of that function - including second- and third-party calls - and if you just miss one, there is still the possibility of a deadlock. The best solution for the problem of calling an asynchronous function from a synchronous function is to change the calling function to be asynchronous (go "async all the way"). Sometimes this isn't possible and you need a hack, but there is no hack that works in every scenario.
  2. The deadlock issue you're trying to avoid has two ingredients: blocking a thread on a task, and a single-threaded context. Console applications do not have a single-threaded context, so in a Console application it's as though all your code uses ConfigureAwait(false) everywhere.

My question is how can I achieve the same result by calling the async function synchronized like the first signature I wrote for the OnSomething function?

The calling code will either block on the asynchronous code (e.g., Wait), or it will not (e.g., await). You can't block on the asynchronous code and continue executing. The calling thread must either continue executing or block; it can't do both.

Upvotes: 4

Johnathan Barclay
Johnathan Barclay

Reputation: 20354

It's because when you call _somethingEvent(null, 10);, OnSomething isn't being awaited, so the following Console.WriteLine("fetch some other resources"); will be allowed to run straight after await FakeAsync(); is executed.

If you don't need to await the completion of OnSomething before continuing, just do a fire and forget:

private static void OnSomething(object sender, int e)
{
    Console.WriteLine($"passed value : {e}");
    Task.Run(() => FakeAsync());
}

Upvotes: 1

Related Questions