简体   繁体   English

如何在不阻塞主线程的情况下调用异步函数同步?

[英]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.我编写了一个简单的控制台应用程序来测试ConfigureAwait(false)以解决以同步方式调用异步函数的问题。 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:当我像这样将OnSomething函数更改为 async void 时:

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?我的问题是如何通过像我为OnSomething函数编写的第一个签名一样调用同步的异步函数来获得相同的结果?
How could I use ConfigureAwait(false) to help me in this situation?在这种情况下,我如何使用ConfigureAwait(false)来帮助我? Or maybe I am not using it properly.或者也许我没有正确使用它。

I wrote a simple console application to test ConfigureAwait(false) for fixing the problem of calling an async function in a synchronized way.我编写了一个简单的控制台应用程序来测试 ConfigureAwait(false) 以解决以同步方式调用异步函数的问题。

There's a couple of problems with this.这有几个问题。

  1. ConfigureAwait(false) is not a "fix" for the "problem" of calling an asynchronous function synchronously. ConfigureAwait(false)不是同步调用异步函数的“问题”的“修复”。 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.这是在某些情况下可行的一种可能的 hack ,但通常不建议这样做,因为您必须在该函数的整个传递闭包中的任何地方使用ConfigureAwait(false) - 包括第二方和第三方调用 - 如果您只是错过了一个,还是有死锁的可能。 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.有时这是不可能的,您需要hack ,但没有适用于所有场景的 hack 。
  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.控制台应用程序没有单线程上下文,因此在控制台应用程序中,就好像您的所有代码在任何地方都使用ConfigureAwait(false)

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?我的问题是如何通过像我为 OnSomething 函数编写的第一个签名一样调用同步的异步函数来获得相同的结果?

The calling code will either block on the asynchronous code (eg, Wait ), or it will not (eg, await ).调用代码要么阻塞异步代码(例如Wait ),要么不阻塞(例如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.它不能两者兼而有之。

It's because when you call _somethingEvent(null, 10);这是因为当你调用_somethingEvent(null, 10); , OnSomething isn't being awaited, so the following Console.WriteLine("fetch some other resources"); , OnSomething没有被等待,所以下面的Console.WriteLine("fetch some other resources"); will be allowed to run straight after await FakeAsync();将允许在await FakeAsync();后直接运行await FakeAsync(); is executed.被执行。

If you don't need to await the completion of OnSomething before continuing, just do a fire and forget:如果您在继续之前不需要等待OnSomething的完成,只需触发并忘记:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM