Task.WhenAll、异步与多线程、异步编程注意点、异步与yield
- 异步编程和多线程
多线程只是异步的一种实现方式,异步编程也可是单一线程,比如async方法中的await执行时,当前线程会被线程池收走,等方法await的方法执行完成时,线程池会调度一个新的线程过来执行下面的逻辑;此过程虽然切换了线程,但是同一时间运行的线程只有一个,并不是多线程。
- Task.WhenAll
- Task类有两个静态方法,WhenAny()和WhenAll();WhenAny() 代表所有异步task任务中只要有一个执行完成,就执行;WhenAll是代表必须等待所有异步task任务执行完,才走后面的逻辑。一般项目中WhenAll()方法用的比较多。
- Task.WhenAll()等待所有的Task执行完成,但是这些Task不是同步顺序执行的,而是异步一起执行的,开启多个线程异步执行,可以加快程序效率,如果项目中遇到耗时的重复代码时,可以考虑用Task.WhenAll;
比如下面两个例子:
第一个:等所有衣服异步洗完之后,执行提示“所有衣服都洗完了”;
第二个:读取指定目录下的文件中的字符长,等待所有异步读完之后,打印出总的字符串的长度
private void button5_Click(object sender, EventArgs e)
{
List<Task> tasks = new List<Task>();
tasks.Add(Task.Run(() =>
{
MessageBox.Show("上衣洗好了");
}));
tasks.Add(Task.Run(() =>
{
MessageBox.Show("裤子洗好了");
}));
tasks.Add(Task.Run(() =>
{
MessageBox.Show("袜子洗好了");
}));
Task.WhenAll(tasks).ContinueWith(t => {
MessageBox.Show($"衣服都洗好了,可以晾了");
});
}
{
class Program
{
static async Task Main(string[] args)
{
string[] files = Directory.GetFiles(@"C:\Users\liyumin\Desktop\test");
Task<int>[] strLengthTasks = new Task<int>[files.Length];
for (int i = 0; i < files.Length; i++)
{
Task<int> t = GetFileStringLength(files[i]);
strLengthTasks[i] = t;
}
//等待所有异步任务执行完成
int[] length = await Task.WhenAll(strLengthTasks);
Console.WriteLine($"所有文件的字符串总和为:{length.Sum()}");
}
/// <summary>
/// 异步读取指定文件的字符串长度
/// </summary>
/// <param name="fileName"></param>
/// <returns></returns>
private static async Task<int> GetFileStringLength(string fileName)
{
string text = await File.ReadAllTextAsync(fileName);
return text.Length;
}
}
}
异步编程需要注意的点:
- 接口和抽象方法不能修饰为async,async是提示编译器为了异步方法中的await代码进行分段处理的,而一个异步方法是否修饰了async对于方法的调用者来说没有区别,因此接口方法和抽象方法不能修饰为async。(从反编译的async-await方法的代码看,编译器把异步方法拆分成多部分的状态机代码通过moveNext()方法来执行)
异步与yield
- yield return 相当于流式返回,不需要等待所有集合项获取到之后再统一返回,而是获取一个返回一个,这对于项目中获取一些大型数据集合时比较适合。
private IEnumerable<string> GetData()
{
yield return "1";
yield return "3";
yield return "2";
yield return "4";
yield return "5";
yield return "6";
}
- 在旧版C#中,async不能用来修饰yield,从C#8.0开始,把返回值修饰为IAsyncEnumerable(不带Task),然后遍历使用的时候使用await foreach即可。
调试代码时候,可以发现,集合获取一个,控制台打印一个:
- ASP.Net Core 和控制台项目中没有SynchronizationContext ,因此不用管ConfigureWait(falsed)等,不要同步和异步混用。