c# Async方法一直调用到Main
本文关键字:Main 调用 一直 Async 方法 | 更新日期: 2023-09-27 17:50:42
谁能解释一下这个例子,这当然是行不通的:
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<int> 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<int> GetNumber1()
{
await Task.Run(() =>
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine("GetNumber1");
System.Threading.Thread.Sleep(100);
}
});
return 1;
}
我试图通过使用"await"从GenNumberX方法"收集"值。我想使方法"测试"不是异步的某种方式。我不明白为什么测试必须是异步的,当我使用等待来获得一个值。这个例子让我在使用async的每个方法上都写async,当我钻到Main时,我不能让它async?
如何写真实世界的例子:
public bool ReserveAHolliday()
{
bool hotelOK = await ReserveAHotel();//HTTP async request
bool flightOK = await ReserveFlight();////HTTP async request
bool result = hotelOK && flightOK;
return result;
}
如何使方法ReserveAHolliday同步?我错过了什么或不理解异步等待机制的使用?
下面是一个完整的例子。您可以同步运行reserserveraholiday (bool r = ReserveAHolliday(). result;)和异步运行(只需调用ReserveAHolliday();)从MAIN(取决于哪一行你注释)。您可以看到效果(在预订完成之前/之后打印"END")。我更喜欢await Task.WhenAll()方法,它更具可读性。还要注意,最好使用await Task.Delay(100)而不是Thread。GetNumber1.
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<int> 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<int> 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<bool> 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<bool> 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;
}
}
首先永远不要使用,结果它有一个糟糕的实现和你最终会出现死锁。而是使用GetAwaiter()。getresult ()
如果你使用的是c# 7.0或以下版本,那么使用这个模式是安全的:
static void Main(string[] args)
{
MainAsync(args).GetAwaiter().GetResult();
}
private static async Task MainAsync(string[] args)
{
await MyMethod();
await MyOtherMethod();
Console.WriteLine("Done!");
}
private static async Task MyMethod()
{
// do something
await Task.CompletedTask;
}
private static async Task MyOtherMethod()
{
// do something
await Task.CompletedTask;
}
如果你正在使用c# 7.1+,那么你可以将main方法更改为异步方法,像这样:
static async Task Main(string[] args)
{
await MyMethod();
await MyOtherMethod();
Console.WriteLine("Done!");
}
private static async Task MyMethod()
{
// do something
await Task.CompletedTask;
}
private static async Task MyOtherMethod()
{
// do something
await Task.CompletedTask;
}
在。net核心项目中,你可以使用这个标签来切换到c# 7.1或更高版本:
<LangVersion>7.1</LangVersion>
或
<LangVersion>latest</LangVersion>
项目文件看起来像这样:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
<LangVersion>latest</LangVersion>
</PropertyGroup>
</Project>
谢谢FrankerZ的正确答案!所以我的小例子应该是这样的:
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<int> 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<int> 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
的介绍性博客文章在这里
您的async需要从某个地方开始,并且由于您的程序有一个起点,因此此时它是一个同步方法。大多数async
都是从我们喜欢称之为async void
的方法开始的,它们基本上是即发即忘的方法。它启动一个任务,并不关心返回什么。如果您需要在同步方法中等待某些东西,您可以在任务上使用.Wait()
方法,或者使用.Result
来获取任务的结果。
对于您的实际示例,如果您想同时运行两个任务,则需要执行如下操作:
public async Task<bool> ReserveAHoliday()
{
//Initialize and start this task
Task<bool> hotelTask = ReserveAHotel();//HTTP async request
//Initialize and start this task
Task<bool> 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;
}
我强烈推荐观看这个视频。它很好地介绍了Async是如何工作的,可以让你进入Async的奇妙世界。
如果在ReserveAHolliday方法中使用await关键字,就不能使其同步。
可能只是阻塞,直到两个异步方法都返回它们的值。但我不建议这样做,因为它可能导致死锁或其他意想不到的错误。
如果没有同步上下文(例如:控制台应用程序),您可以简单地写入
bool hotelOK = ReserveAHotel().Result;
但我认为你最终想要创建一个GUI。这就是阻塞失效的地方。因为UI应用程序(Forms和WPF)确实有上下文,等待异步方法会导致死锁。点击这里阅读更多内容
有解决这个问题的方法,但它们高度依赖于应用程序的设计。
编辑:如果你的库代码需要同步上下文,那么阻塞肯定会导致死锁。
如果你不需要它,可以在你的库代码中使用ConfigureAwait(false)
,从而避免阻塞。
我的建议是:一直使用async/await,或者根本不使用async/await。让你的代码异步化还有其他的可能性,例如:基于事件,但就像生活中的每件事都有利弊一样。
请阅读Stephen Cleary的博客。他解释了为什么阻塞异步代码会导致死锁,以及如何避免死锁。