目录
“异步方法”:用async关键字修饰的方法
- 异步方法的返回值一般是Task<T>,T是真正的返回值类型,Task<int>。惯例:异步方法名字以Async结尾
- 即使方法没有返回值,也最好把返回值声明为非泛型的Task
- 调用泛型方法时,一般在方法前加上await关,这样拿到的返回值就是泛型指定的T类型
- 异步方法的“传染性”:一个方法中如果有await调用,则这个方法也必须修饰为async
返回值可为void或Task
static async Task Main(string[] args)
{
string filename="E:\Temp\a.txt";
await File.WriteAllTextAsync(filename,"hello");
string s=await File.ReadAllTextAsync(filename);
}
如果同样的功能,既有同步方法,又有异步方法,尽量使用异步方法
对于不支持的异步方法,Wait()(无返回值),Result()(有返回值)。有死锁风险,尽量不用
异步方法编写
int length = await DownloadHtmlAsync("http://www.baidu.com", @"e:\a.txt");
Console.WriteLine(length);
static async Task<int> DownloadHtmlAsync(string url, string filename)
{
string html = "";
using (HttpClient httpClient = new HttpClient())
{
html = await httpClient.GetStringAsync(url);
await File.WriteAllTextAsync(filename, html);
}
return html.Length;
}
//线程池中的异步委托使用,在lamda表达式前加async修饰就可以使用异步编程
ThreadPool.QueueUserWorkItem(async (obj) =>
{
while (true)
{
await File.WriteAllTextAsync(@"e:\a.txt", "aaaaaaaa");
Console.WriteLine("xxxxxx");
}
});
Console.Read();
注意:
在VS2022中,创建控制台应用主要使用的框架是.NET6.0,而这个只有一个WriteLine语句的控制台,就是.NET6.0的新模板,使用了顶级语句这个功能。只有.Net5.0及其以下版本,才会生成以前带Main和名称空间的模板。
使用顶级语句可直接在文件的根目录中编写可执行代码,而无需在类或方法中包装代码。 这意味着无需使用 Program 类和 Main 方法即可创建程序。 在这种情况下,编译器将使用入口点方法为应用程序生成 Program 类。
.NET6中Main方法的生成
在创建控制台应用时,勾选“不使用顶级语句”
static async Task Main(string[] args)
{
int length = await DownloadHtmlAsync("http://www.baidu.com", @"e:\a.txt");
Console.WriteLine(length);
}
static async Task<int> DownloadHtmlAsync(string url, string filename)
{
string html = "";
using (HttpClient httpClient = new HttpClient())
{
html = await httpClient.GetStringAsync(url);
await File.WriteAllTextAsync(filename, html);
}
return html.Length;
}
async背后的线程切换
await调用的等待期间,.NET会把当前的线程返回给线程池,等异步方法调用执行完毕后,框架会从线程池再取出来一个线程执行后续的代码。
如果写入内容少,会发现线程Id不变。
Thread.CurrentThread.ManagedThreadId:获得当前线程Id
static async Task Main(string[] args)
{
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
StringBulider sb=new StringBulider();
for(int i=0;i<10000;i++)
{
sb.Append("XXXXXXXXXXXXXXXX");
}
await File.WriteAllTextAsync(@"e:\a.txt",sb.ToString());
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
}
接口中的异步方法:
async是提示编译器为异步方法中的await代码进行分段处理的,而一个异步方法是否修饰了async对于方法的调用者来讲没区别,因此对于接口中的方法或抽象方法不能修饰为async
异步与yield:
yield return不仅能够简化数据的返回,而且可以让数据处理“流水线化”,提升性能
static IEnumerable<string> Test()
{
yield return "hello";
yield return "hi";
yield return "ooo";
}
在旧版C#中,async方法中不能用yield。从C#8.0开始,把返回值声明为IAsyncEnumerable(不要带Task),然后遍历的时候用await foreach()即可
static async Task Main(string[] args)
{
await foreach(var s in Test())
{
Console.WriteLine(s);
}
}
static async IAsyncEnumerable<string> Test()
{
yield return "hello";
yield return "hi";
yield return "ooo";
}
ASP.NET Core和控制台项目中没有SynchronizationContext,因此不用管ConfigureAwait(false)等。不要同步、异步混用