C#实现异步多线程的方式有多种,以下总结的是async/await异步方法。
环境准备
基于.NET6创建控制台项目进行测试,下面提前准备几个方法,防止后续举例时代码重复。
//封装一个打印调试方法,输出线程ID和时间戳
private static void LogDebug(string msg)
{
Console.WriteLine($"{
msg} 【ThreadId {
Thread.CurrentThread.ManagedThreadId.ToString("00")}】 {
DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
}
//封装一个方法模拟一个长时间的计算
private static long DoSomethingLong()
{
long lResult = 0;
for (int i = 0; i < 500000000; i++)
{
lResult += i;
}
return lResult;
}
//封装一个方法产生一个很大的字符串用于模拟耗时的写入文件操作
private static string GenBigString()
{
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < 1000000; i++)
{
stringBuilder.Append("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
}
return stringBuilder.ToString();
}
编写异步方法
- async用于修饰方法声明,await出现在Task对象或泛型Task<T>对象前面。
- async/await 通常是成对出现的,只有async没有await没什么意义,执行结果和不加async的普通方法没有区别;有await调用,则这个方法必须修饰为async,否则会报错。
- 异步方法名字一般以Async结尾。
无返回值
- 没有返回值的情况,最好把返回值声明为非泛型的Task而不是void,方法体我们仍按照无返回值去编写,无需手动return一个Task对象。
private static async Task NoReturnAsync(string srcFile, string destFile)
{
string context = await File.ReadAllTextAsync(srcFile);
await File.WriteAllTextAsync(destFile, context);
}
static async Task Main(string[] args)
{
LogDebug("Project start");
await NoReturnAsync(@"E:\temp\LambdaAsync.txt", @"E:\temp\LambdaAsync.cp.txt");
LogDebug("Copy complete");
Console.Read();
}
有返回值
- 有返回值的异步方法,返回值为 Task<T>,T是真正的返回类型。
- 调用异步方法时,在 Task<T> 对象前面加上await关键字,这样拿到的结果就是泛型指定的T类型。
- 在使用return返回结果时,我们只要返回泛型 Task<T>指定的T类型即可。
private static async Task<bool> WithReturnAsync(string srcFile, string destFile)
{
bool result = false;
//读取源文件
string srcFileContext = await File.ReadAllTextAsync(srcFile);
int srcFileLength = srcFileContext.Length;
//读取内容写入目标文件
await File.WriteAllTextAsync(destFile, srcFileContext);
//读取目标文件
string destFileContext = await File.ReadAllTextAsync(destFile);
int destFileLength = destFileContext.Length;
//比较源文件和目标文件长度是否相同
if (destFileLength == srcFileLength)
result = true;
return result;
}
static async Task Main(string[] args)
{
LogDebug("Project start");
bool result = await WithReturnAsync(@"E:\temp\LambdaAsync.txt", @"E:\temp\LambdaAsync.cp.txt");
if (result)
LogDebug("文件拷贝成功");
else
LogDebug("文件拷贝失败");
Console.Read();
}
异步委托
如果在lambda表达式中需要调用异步方法,可以用async把这个lambda修饰为异步,否则会报错(The ‘await’ operator can only be used within an async lambda expression. Consider marking this lambda expression with the ‘async’ modifier.)。
private static Task LambdaAsync(string fileName, string str)
{
return Task.Run(async () => await File.WriteAllTextAsync(