C#异步方法一直调用Main

有人可以澄清这个例子,当然,这个例子不起作用:

class Program { static void Main(string[] args)//main cant' be async { int res = test();//I must put await here Console.WriteLine("END"); } public async static Task test() { //why can't I make it just: public int test()?? int a1, a2, a3, a4; a1 = await GetNumber1(); a2 = await GetNumber2(); a3 = await GetNumber3(); a4 = a1 + a2 + a3; return a4; } public static async Task GetNumber1() { await Task.Run(() => { for (int i = 0; i < 10; i++) { Console.WriteLine("GetNumber1"); System.Threading.Thread.Sleep(100); } }); return 1; } 

我试图通过使用“await”从GenNumberX方法“收集”值。 我想让方法“测试”不是以某种方式异步。 我不明白为什么当我使用await获取值时,测试必须是异步的。 这个例子让我在每个使用异步的方法上编写异步,当我向上钻取Main时,我不能让它异步?

如何编写真实世界的例子:

 public bool ReserveAHolliday() { bool hotelOK = await ReserveAHotel();//HTTP async request bool flightOK = await ReserveFlight();////HTTP async request bool result = hotelOK && flightOK; return result; } 

如何使方法ReserveAHolliday同步? 我错过了什么或者不理解async-await机制的使用?

下面是一个完整的例子。 您可以同步运行ReserverAHoliday(bool r = ReserveAHolliday()。结果;)和异步(只需从MAIN调用ReserveAHolliday();)(取决于您评论的哪一行)。 并且您可以看到效果(在预订完成之前/之后打印“END”)。 我更喜欢await Task.WhenAll()方法,它更具可读性。 另请注意,在GetNumber1中使用await Task.Delay(100)而不是Thread.sleep是首选。

  class Program { static void Main(string[] args)//main cant' be async { //int res = test().Result;//I must put await here bool r = ReserveAHolliday().Result; //this will run Synchronously. //ReserveAHolliday(); //this option will run aync : you will see "END" printed before the reservation is complete. Console.WriteLine("END"); Console.ReadLine(); } public async static Task test() { //why can't I make it just: public int test()?? //becuase you cannot use await in synchronous methods. int a1, a2, a3, a4; a1 = await GetNumber1(); a2 = await GetNumber1(); a3 = await GetNumber1(); a4 = a1 + a2 + a3; return a4; } public static async Task GetNumber1() { //await Task.Run(() => // { for (int i = 0; i < 10; i++) { Console.WriteLine("GetNumber1"); await Task.Delay(100); // from what I read using Task.Delay is preferred to using System.Threading.Thread.Sleep(100); } // }); return 1; } public async static Task ReserveAHolliday() { //bool hotelOK = await ReserveAHotel();//HTTP async request //bool flightOK = await ReserveAHotel();////HTTP async request var t1 = ReserveAHotel("FirstHotel"); var t2 = ReserveAHotel("SecondHotel"); await Task.WhenAll(t1, t2); bool result = t1.Result && t1.Result;// hotelOK && flightOK; return result; } public static async Task ReserveAHotel(string name) { Console.WriteLine("Reserve A Hotel started for "+ name); await Task.Delay(3000); if (name == "FirstHotel") await Task.Delay(500); //delaying first hotel on purpose. Console.WriteLine("Reserve A Hotel done for " + name); return true; } } 

您的异步需要从某处开始,并且由于您的程序有一个起点,因此此时它是一种同步方法。 大多数async都是从我们喜欢的async void的,它们基本上是fire和forget方法。 它启动一项任务,并不关心它返回什么。 如果需要在同步方法中等待某些内容,可以在任务上使用.Wait()方法,或使用.Result获取任务的结果。

对于您的真实示例,如果您想同时运行这两个任务,则需要执行以下操作:

 public async Task ReserveAHoliday() { //Initialize and start this task Task hotelTask = ReserveAHotel();//HTTP async request //Initialize and start this task Task flightTask = ReserveFlight();////HTTP async request //Await until hotel task is done, and get the bool bool hotelOK = await hotelTask; //Await until flight task is done (This might have already finished while the hotel was grabbing, and get the bool bool flightOK = await flightTask; bool result = hotelOK && flightOK; //Return the final result return result; } 

我强烈建议观看此video 。 它很好地介绍了Async如何工作,并且可以在异步的精彩世界中启动你。

感谢FrankerZ的正确答案! 所以我的litle示例应该是这样的:

 class Program { static void Main(string[] args) { var task = test(); task.Wait(); //this stops async behaviour int result = task.Result;// get return value form method "test" Console.WriteLine("RESULT IS = " + result); } public async static Task test() { int a1, a2, a3, a4; //run all tasks //all tasks are doing their job "at the same time" var taskA1 = GetNumber1(); var taskA2 = GetNumber2(); var taskA3 = GetNumber3(); //wait for task results //here I am collecting results from all tasks a1 = await taskA1; a2 = await taskA2; a3 = await taskA3; //get value from all task results a4 = a1 + a2 + a3; return a4; } public static async Task GetNumber1() { await Task.Run(() => { for (int i = 0; i < 10; i++) { Console.WriteLine("GetNumber1"); System.Threading.Thread.Sleep(100); } }); return 1; } 

//其他方法被省略,因为它们与GetNumber1相同

为了在方法中使用await关键字,它必须是async 。 这是一个设计决策,使其更容易向后兼容。

Main不能是async因为它是程序的起点,所以你通常需要以某种方式阻止程序运行。 您可以通过阻止Task或甚至通过调用Console.ReadLine

任务通常在后台线程上执行,如果所有前台线程都停止,后台线程不会使程序保持运行。

我在这里有关于async-await的介绍性博客文章

如果在其中使用await关键字,则无法使方法ReserveAHolliday成为同步方法。

可能的是简单地阻塞,直到两个异步方法返回它们的值。 我不推荐它,因为它可能导致死锁或其他意外错误。

如果您没有同步上下文(例如控制台应用程序),您可以简单地编写

 bool hotelOK = ReserveAHotel().Result; 

但我估计你最终想要创建一个GUI。 这就是阻塞分崩离析的地方。 因为UI应用程序(Forms和WPF)确实有上下文并且等待异步方法导致死锁。 在这里阅读更多

有这个问题的解决方法,但它们高度依赖于您的应用程序的设计。

编辑:

如果您的库代码需要同步上下文,那么阻塞肯定会导致死锁。

如果您不需要它,可以在库代码中使用ConfigureAwait(false)并远离阻塞。

我建议的是:async / await or或者根本没有async / await。 还有其他可能使你的代码异步,例如:基于事件,但像生活中的一切都有利有弊。

请阅读Stephen Cleary的这篇博客 。 他解释了为什么阻止异步代码会死锁以及如何避免它。