并发系列64章(异步编程二)第三章
- 2020 年 4 月 8 日
- 筆記
前言
是在第二章基础上续写的,主要是完结第二章例子部分。
请看下面几个例子,感受一下。
报告进度
不管我们完任何app,每次更新的时候都能看到进度条。
而我们知道ui界面更新,一般来说是和更新程序异步的,但是更新程序又要通知ui进度。
代码:
public class Program { static double percentComplete = 0; static void Main(string[] args) { doit(); Console.ReadKey(); } public static void DoLoop() { Task.Run(() => { for (var i = 1; i <= 100000; i++) { setPercent(i/1000); Console.WriteLine(i); } setPercent(101); }); } public async static void doit() { var pregress = CallProcessTask(); var task=MyMethodAsync(pregress); DoLoop(); await task; } public static void setPercent(int percent) { percentComplete = percent; } public static Progress<double> CallProcessTask() { var progress = new Progress<double>(); progress.ProgressChanged += (sender, args) => { //在这里做一些进度变化,显示在ui上。 Console.WriteLine("process:"+args); }; return progress; } public static Task MyMethodAsync(IProgress<double> progress=null) { var task= Task.Run(() => { while (percentComplete <= 100) { if (progress != null) { Console.WriteLine("查看进度:" + percentComplete); progress.Report(percentComplete); } } }); return task; } }
上面我想做的事,一个异步的事件——DoLoop。
可能初学者,很喜欢用async 和 await ,可能会认为没有async 和 await的就不是异步事件。
async和 await的存在的作用就在于等待当前异步事件完成,如果没有这个需求,那么你为什么要写呢?
MyMethodAsync 是另一个异步事件,里面做的事监听当前进度。
CallProcessTask是构造一个progress,并写下委托,监听percentComplete的改变。
等待一组任务完成
static void Main(string[] args) { int[] result = DoAll().Result; Console.ReadKey(); } public static async Task<int[]> DoAll() { Task<int> task1 = Task.FromResult(1); Task<int> task2 = Task.FromResult(2); Task<int> task3 = Task.FromResult(3); int[] result=await Task.WhenAll<int>(task1, task2, task3); return result; }
WhenAll 是一个新的task,管理一组Task。监听一组task进度,当全部的task结束,这个task也结束。
使用await的时候,但是当有一个whenall 管理的task果然有多个task失败,那么只会报一个错误。
而使用whenall,我们的需求是要全部成功,要是有一个不成功那么也应该是失败的。所以我们只要截获一个错误是正确的,系统也是这么做的。
下面是截获代码:
public static async Task<int[]> DoAll() { Task<int> task1 = Task.FromResult(1); Task<int> task2 = Task.FromResult(2); Task<int> task3 = Task.FromResult(3); Task<int[]> Alltasks = Task.WhenAll<int>(task1, task2, task3); try { var result=await Alltasks; return result; } catch { AggregateException allException = Alltasks.Exception; // 处理错误 } return null; }
等待任意一个任务完成
为啥会有这种需求?可能你觉得我们每次访问的时候都是对一条url,比如说我们访问百度,那么我们不就是访问www.baidu.com。
但是呢,有几个运营服务商,做的相同的业务,都是免费的,但是公司保险起见呢,一起调用,为了确保在不同地域的请求稳定。
static void Main(string[] args) { var result = DoAny('www.baidu.com','www.baidu.com').Result; Console.ReadKey(); } public static async Task<byte[]> DoAny(string urlA,string urlB) { var httpClient = new HttpClient(); Task<Byte[]> DownLoadTaskA = httpClient.GetByteArrayAsync(urlA); Task<Byte[]> DownLoadTaskB = httpClient.GetByteArrayAsync(urlB); var completedTask =await Task.WhenAny<byte[]>(DownLoadTaskA, DownLoadTaskB); return await completedTask; }
这里值得关注的是为什么有两个await:
var completedTask =await Task.WhenAny<byte[]>(DownLoadTaskA, DownLoadTaskB); return await completedTask;
因为创建WhenAny 的Task是异步的,而创建 whenAll 的Task 是同步的。
值得注意的是whenAny 当一个任务完成时,那么会返回这个Task。其他任务依然还是在执行,需要考虑的是中断一个task更好,还是直接让他运行完更好。这是需要考虑的地方。
任务完成后处理
比如说有3个任务,我希望只要完成任意一个任务完成后马上接下来完成它的后续任务。
如果我这样写的话:
static void Main(string[] args) { ProcessTasksAsyns(); Console.ReadKey(); } public static async Task ProcessTasksAsyns() { Task<int> TaskA = DelayAndReturnAsync(2); Task<int> TaskB = DelayAndReturnAsync(3); Task<int> TaskC = DelayAndReturnAsync(1); Task<int>[] tasks = new[] { TaskA, TaskB, TaskC }; foreach (var task in tasks) { var result = await task; Console.WriteLine("后续执行:"+result); } } static async Task<int> DelayAndReturnAsync(int val) { await Task.Delay(TimeSpan.FromSeconds(val)); Console.WriteLine(val); return val; }
但是得到的却是这样的结果。
这个结果显示Task 完成的顺序是 1 ,2,3秒。
但是执行后续的顺序是await的顺序,也就是2,3,1.
解决方案是将3个任务,分别放在另外3个任务中执行。
static void Main(string[] args) { ProcessTasksAsyns(); Console.ReadKey(); } public static async Task ProcessTasksAsyns() { Task<int> TaskA = DelayAndReturnAsync(2); Task<int> TaskB = DelayAndReturnAsync(3); Task<int> TaskC = DelayAndReturnAsync(1); Task<int>[] tasks = new[] { TaskA, TaskB, TaskC }; var processingTasks = tasks.Select(async t => { var result = await t; Trace.WriteLine(result); }); await Task.WhenAll(processingTasks); } static async Task<int> DelayAndReturnAsync(int val) { await Task.Delay(TimeSpan.FromSeconds(val)); Console.WriteLine(val); return val; }
当然后面会介绍到其他方法,但是这个解释了,如果去实现这种需求。
未完
还有几个例子,下章结束例子。