示例:
static static void Main(string[] args)
{
//同步方法
{
string filename = @"C:\TEMP\1.txt";
File.WriteAllText(filename, "hello world");
string strContext = File.ReadAllText(filename);
Console.WriteLine(strContext);
}
}
static async Task Main(string[] args)
{
//异步方法
{
string filename = @"C:\TEMP\1.txt";
await File.WriteAllTextAsync(filename, "hello world");
string strContext = await File.ReadAllTextAsync(filename);
Console.WriteLine(strContext);
}
}
上面两段代码执行结果是一样的,都是往txt文本中写入一段文字并读取出来。
async,await基本使用总结:
1.异步方法的返回值一般是Task<T>,T是真正的返回值类型,Task<int>。异步方法名字以Async结尾。
2.即使没有返回值,也最好把返回值声明为非泛型的Task。
3.调用异步方法时,一般在方法前面加上await,这样拿到的返回值就是泛型指定的T类型。
4.异步方法的“传染性”,一个方法中如果有await调用,则这个方法也必须修饰为async。
5.如果一个方法既有同步方法,又有异步方法,尽量调用异步方法。
6.async,await不等于“多线程” 。
调用await之前和之后可能不是一个线程在执行程序,下面做一个测试,在await语句执行前后分别打印出线程的ID。
/// <summary>
/// 线程ID测试
/// </summary>
/// <returns></returns>
public static async Task ThreadTest()
{
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++)
{
sb.Append("XXXXXXXXXXXXXXXXXXXXX");
}
await File.WriteAllTextAsync(@"C:\TEMP\2.txt", sb.ToString());
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
}
方法调用:
static async Task Main(string[] args)
{
await ThreadTest();
}
执行结果:

await调用的等待期间,.net会把当前的线程返回给线程池,等异步方法调用执行完毕后,框架会从线程池再取出来一个线程执行后面的代码。
如果在await语句执行时间很短,可能前后还是同一个线程ID。
异步方法的代码并不会自动在新线程中执行,除非把代码放到新线程中执行。
/// <summary>
/// 自定义的异步方法
/// </summary>
/// <param name="n"></param>
/// <returns></returns>
public static async Task<double> CalcAsync(int n)
{
Console.WriteLine("方法中的线程ID:" + Thread.CurrentThread.ManagedThreadId);
double result = 0;
Random rand = new Random();
for (int i = 0; i < n * n; i++)
{
result += rand.NextDouble();
}
return result;
}
调用:
static async Task Main(string[] args)
{
Console.WriteLine("调用之前的线程ID:" + Thread.CurrentThread.ManagedThreadId);
double rel = await CalcAsync(5000);
Console.WriteLine($"rel={rel}");
Console.WriteLine("调用之后的线程ID:" + Thread.CurrentThread.ManagedThreadId);
}
执行结果:
自定义一个异步方法名称为CalcAsync,然后在main方法中调用该异步方法,在异步方法调用前后都打印出线程ID,最后发现为同一线程在处理程序。这样说明了不是调用异步方法就会使用新的线程。
如果要想使方法在新的线程中执行,需要把要执行的代码以委托的形式传递给Task.Run(),这样就会从线程池中取出一个线程执行我们的委托,格式如下:
await Task.Run(()=>{
//耗时操作代码,可以return返回值
})
/// <summary>
/// 自定义的异步方法
/// </summary>
/// <param name="n"></param>
/// <returns></returns>
public static async Task<double> CalcAsync(int n)
{
return await Task.Run(() =>
{
Console.WriteLine("方法中的线程ID:" + Thread.CurrentThread.ManagedThreadId);
double result = 0;
Random rand = new Random();
for (int i = 0; i < n * n; i++)
{
result += rand.NextDouble();
}
return result;
});
}
调用:
static async Task Main(string[] args)
{
Console.WriteLine("调用之前的线程ID:" + Thread.CurrentThread.ManagedThreadId);
double rel = await CalcAsync(5000);
Console.WriteLine($"rel={rel}");
Console.WriteLine("调用之后的线程ID:" + Thread.CurrentThread.ManagedThreadId);
}
执行结果:
调用异步方法前的线程ID为1,执行异步操作的线程ID为4。执行异步操作时使用的是新的线程。