.Net Core异步编程
程序员文章站
2022-03-09 13:41:43
...
.Net Core异步编程
一. 用async关键字修饰的方法
- 异步方法的返回值一般是Task,T是真正的返回值类型,Task.惯例:异步方法名字最好以Async结尾,这样方便我们只要是async
- 即使方法没有返回值,也最好吧返回值声明为非泛型的Task.
- 调用泛型方法时,一般在方法前加上await关键字,这样拿到的返回值就是泛型指定的T类型;
- 异步方法的"传染性": 一个方法中如果await调用,则这个方法也必须修饰为async
static async Task Main(string[] args)
{
string fileName = "d:/1.txt";
File.Delete(fileName);
File.WriteAllTextAsync(fileName,"hello async");
string s = await File.ReadAllTextAsync(fileName);
Console.WriteLine(s);
}
static async Task Main(string[] args)
{
/*string fileName = @"F:\a\1.txt";
File.WriteAllText(fileName, "hello");
string s = File.ReadAllText(fileName);
Console.WriteLine(s);*/
string fileName = @"F:\a\1.txt";
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++)
{
sb.AppendLine("hello");
}
//这里如果不加await,会显示异常 因为还没写入完成就开始读取,读取是独占的
await File.WriteAllTextAsync(fileName, sb.ToString());
string s = await File.ReadAllTextAsync(fileName);
//Task<string> t = File.ReadAllTextAsync(fileName);
//string s = await t;
Console.WriteLine(s);
}
如果同样的功能,既有同步方法,又有异步方法,那么首先使用异步方法. .Net5中,很多框架的方法也都支持异步:Main,WinForm时间处理函数.
对于不支持异步方法该怎么办?
Wait()(无返回值); Result(有返回值) 风险:死锁,尽量不用
static async Task Main(string[] args)
{
int l = await DownloadHtmlAsync("https://www.youzack.com", @"F:\a\1.txt");
Console.WriteLine("ok" + l);
}
/*static async Task DownloadHtmlAsync(string url, string fileame)
{
using (HttpClient httpClient = new HttpClient())
{
string html = await httpClient.GetStringAsync(url);
await File.WriteAllTextAsync(fileame, html);
}
}*/
static async Task<int> DownloadHtmlAsync(string url, string fileame)
{
using (HttpClient httpClient = new HttpClient())
{
string html = await httpClient.GetStringAsync(url);
await File.WriteAllTextAsync(fileame, html);
return html.Length;
}
}
二. await关键字修饰的方法
await调用的等待期间,.NET会把当前的线程返回给线程池,等异步方法调用执行完毕后,框架会从线程池在取出来一个线程执行后续的代码.
Thread.CurrentThread.ManageThreadId 获得当前线程Id
验证:在耗时异步(写入大字符串)操作前后分别打印线程Id
static async Task Main(string[] args)
{
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++)
{
sb.Append("XXXXXXXXXXXXXXXXX");
}
await File.WriteAllTextAsync(@"F:\a\1.txt", sb.ToString());
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
}
优化:到要等待的时候,如果发现已经执行结束了,那就没必要再切换线程了,剩下的代码就继续在之前的线程上就行执行了
三.异步方法不等于多线程
static async Task Main(string[] args)
{
Console.WriteLine("之前" + Thread.CurrentThread.ManagedThreadId);
double r = await CalcAsync(5000);
Console.WriteLine($"r = {r}");
Console.WriteLine("之后" + Thread.CurrentThread.ManagedThreadId);
}
public static async Task<double> CalcAsync(int n)
{
Console.WriteLine("CalcAsync," + Thread.CurrentThread.ManagedThreadId);
double result = 0;
Random rand = new Random();
for (int i = 0; i < n*n; i++)
{
result += rand.NextDouble();
}
return result;
}
结论: 异步方法的代码并不会自动在新线程中执行,除非把代码放到新线程中执行.
把要执行的代码以委托的形式传递给Task().Run().这样就会从线程池中出去一个线程执行我们的委托
await Task.Run(()=>{
//耗时操作代码,可以返回return返回值
})
推荐阅读