一、不要用sleep()
如果想在异步方法中暂停一段时间,不要用Thread.sleep(),因为它会阻塞调用线程,而要用await.Task.Delay()。
举例:
下载一个网址,3秒后下载另一个
示例:
sleep()
为了能直观看到效果,使用winform项目
此时点击按钮之后,页面便卡死不动了,不能在进行操作。因为Sleep()方法,阻塞了主线程。
睡眠结束后,才能继续操纵界面
注释后,便能在下载过程中,继续操作页面
await task.delay()
不会阻塞主线程
此时界面也是可正常操作
在控制台中没看到区别,但是放到WinForm程序中就能看到区别了ASP.NET Core中也看不到区别但是sleep()会降低并发
二、CancellationToken
概念:
多线程一般用于处理比较耗时的操作,
有时需要提前终止任务,比如:
请求超时、用户取消请求。很多异步方法都有
CancellationToken参数,用于获得提前终止执行的信号。
(下载文件暂停,用户关闭网页请求终止)
方法:
CancellationToken结构体
None:空
()bool lsCancellationRequested是否取消(用于判断请求者,是否终止请求)
Register(Action callback)注册取消监听
()Throw lfcancellationRequested()如果任务被取消,执行到这句话就抛异常
CancellationTokenSource
CancelAfter()超时后发出取消信号
Cancel()发出取消信号
CancellationToken Token
示例:
为“下载一个网址N次”的方法增加取消功能。
分别用GetstringAsync+IsCancellationRequested、GetStringAsync+ThrowlfCancellatiorRequested()、带CancellationToken的GetAsync()分别实现:取消分别用超时、用户敲按键(不能await)实现。
1.普通方式
运行结果:
下载一百遍终止。
2.加入cancellationtoken
调用
调用结果:
请求被取消
3.cancellationToken.ThrowIfCancellationRequested()
运行结果,会抛出异常
4.调用封装好的getAsync()方法
运行结果
抛出异常
cancel() 手动取消
ASP.NET Core开发中,一般不需要自已处理CancellationToken.
CancellationTokenSource这些,只要做到“能转发CancellationToken就转发即可。ASP.NET Core会对于用户请求中断进行处理。
(*)演示一下ASP.NETCore中的使用:写一个方法,Delay1000次,用Debug.WriteLine()输出,访问中间跳到放到其他网站。
访问其他网站的时候,就会抛出异常,暂停下载
三、WhenAll()
Task类的重要方法:
- Task WhenAny(lEnumerabletasks)等,任何一个Task完成,Task就完成
- Task<TResult[]> WhenAll(paramsTask[]tasks)等,所有Task完成,Task才完成。用于等待多个任务执行结束,但是不在乎它们的执行顺序。
- FromResult()创建普通数值的Task对象。
示例
Task tl =File.ReadAllTextAsync(“d:/l.txt”);
Task t2 =File.ReadAllTextAsync(“d:/2.txt”);
Task t3 =File.ReadAllTextAsync(“d:/3.txt”);
string[] results = await Task.WhenAll(t1, t2, t3)
string s1 = results[0];
string s2 = results[1];
string s3 = results[2];
1.whenall
运行结果
案例:
计算一个文件夹下,所有文本文件的单词个数汇总。
运行结果
四、异步其他问题
接口中的异步方法:
async是提示编译器为异步方法中的await代码进行分段处理的,而一个异步方法是否修饰了async对于方法的调用者来讲没区别的,因此对于接口中的方法或者抽象方法不能修饰为async。
在实现类中添加async
异步与yield:
复习: yield return不仅能够简化数据的返回,而且可以让数据处理“流水线化”,提升性能。
static lEnumerable<string> Test()
{
yield return "hello",
yield return "'yzk" ;
yield return "'youzack";
}
运行结果
在旧版C#中,async方法中不能用yield。
从C#8.0开始,把返回值声明为IAsyncEnumerable(不要带Task),然后遍历的时候用await foreach()即可。
static async Task Main(string[] args)
{
await foreach(var s in Test())
Console.WriteLine(s);
}
static async lAsyncEnumerable<string> Test()
{
yield return "hello";
yield return "yzk";
yield return "youzack";
}