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

时间:2020-02-18 13:45:47

标签: c# asynchronous async-await

我编写了一个简单的控制台应用程序来测试 ConfigureAwait(false),以解决以同步方式调用异步函数的问题。这就是我模拟事件并尝试以同步方式调用异步函数的方式。

 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");
        }
    }

代码输出如下:

passed value : 10
task completed
fetch some other resources

然后当我将 OnSomething 函数更改为异步void时:

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

输出将更改为:

passed value : 10
fetch some other resources
task completed

您可以看到任务完成部分之前发生了获取一些其他资源,这是我想要的结果。
我的问题是如何通过调用异步函数来实现相同的结果,就像我为 OnSomething 函数编写的第一个签名一样?
在这种情况下,如何使用 ConfigureAwait(false)来帮助我?也许我没有正确使用它。

2 个答案:

答案 0 :(得分:3)

我编写了一个简单的控制台应用程序来测试ConfigureAwait(false),以解决以同步方式调用异步函数的问题。

这有两个问题。

  1. ConfigureAwait(false)不是同步调用异步函数的“问题”的“解决方案”。 one possible hack在某些情况下可以使用,但一般不建议这样做,因为在该函数的整个传递闭包中,您必须使用ConfigureAwait(false) 无处不在-包括第二和第三通话-如果您只错过一个 ,仍然有可能出现死锁。解决从同步函数调用异步函数的问题的最佳解决方案是change the calling function to be asynchronous(一直“异步”)。有时这是不可能的,您需要使用hack,但没有适用于每种情况的技巧。
  2. deadlock issue you're trying to avoid具有两个成分:阻塞任务中的线程和单线程上下文。控制台应用程序没有单线程上下文,因此在控制台应用程序中,好像您的所有代码在各处都使用ConfigureAwait(false)

我的问题是如何通过调用异步函数(如我为OnSomething函数编写的第一个签名)同步来获得相同的结果?

调用代码将阻塞异步代码(例如Wait),或者不会阻塞(例如await)。您无法阻止异步代码,并且继续执行。调用线程必须继续执行或阻塞;它不能同时做到。

答案 1 :(得分:1)

这是因为当您调用_somethingEvent(null, 10);时,没有等待OnSomething,因此下面的Console.WriteLine("fetch some other resources");将被允许在执行await FakeAsync();之后直接运行。

如果您无需等待OnSomething的完成就可以继续,那就开火忘了:

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