8 async/await异步编程基本使用

379 阅读2分钟

用async关键字修饰的方法,即为“异步方法”。以下几点需要注意:

  1. 异步方法的返回值一般是Task<T>,T是真正的返回值类型,如Task<int>。惯例:异步方法名字以Async结尾。

  2. 即使方法没有返回值,也最好把返回值声明为非泛型的Task。

  3. 调用泛型方法时,一般在方法前加上await,这样拿到的返回值就是泛型指定的T类型。

  4. 异步方法的“传染性”:一个方法中如果有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);
}

为什么要在调用方法前加上await?
下面我们上代码,看看加不加await有什么区别。

//异步(不加await)
string fileName = "G:/1.txt";
File.WriteAllTextAsync(fileName, "Hello!");

//Task<string> t = File.ReadAllTextAsync(fileName);
//string a = await t;

//推荐这种写法
string a = await File.ReadAllTextAsync(fileName);
Console.WriteLine(a);

运行结果:

image.png

//异步(加await)
string fileName =  "G:/1.txt";
await File.WriteAllTextAsync(fileName, "Hello!");

//Task<string> t = File.ReadAllTextAsync(fileName);
//string a = await t;

//推荐这种写法
string a = await File.ReadAllTextAsync(fileName);
Console.WriteLine(a);

运行结果:

image.png

都可以正常编译运行,看起来没什么不同。下面我们换个大字符串试试。
//异步,写入大字符串(不加await)
using System.Text;

string fileName = "G:/2.txt";
StringBuilder t = new StringBuilder();
for (int i = 0; i < 10000; i++)
{
    t.AppendLine("Hello!");
}
File.WriteAllTextAsync(fileName, t.ToString());
string a = await File.ReadAllTextAsync(fileName);
Console.WriteLine(a);

运行结果:

image.png

//异步,写入大字符串(不加await)
using System.Text;

string fileName = "G:/软件开发/.net 6/.NET6_杨中科/练习/Part2-4/2.txt";
StringBuilder t = new StringBuilder();
for (int i = 0; i < 10000; i++)
{
    t.AppendLine("Hello!");
}
await File.WriteAllTextAsync(fileName, t.ToString());
string a = await File.ReadAllTextAsync(fileName);
Console.WriteLine(a);

运行结果:

image.png

写入大字符串时,如果不加await,会抛出异常。原因就是,写入方法还没有执行完,就往下执行了读取方法。加上await,会等写入方法执行结束后,再往下执行读取。

结论: 调用泛型异步方法时,要在方法前加上await