在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
原文链接:https://blog.csdn.net/zuheyawen/article/details/99863588 前言 什么是异步方法 3. Task 分为两种: 4. 异步方法的返回类型必须为 void、Task、Task<TResult> 中的其中一种。 归纳一下:void 不关心结果;Task 只关心是否执行完成;Task<TResult> 不止关心是否执行完成,还要获取执行结果。 /// <summary> /// 扔垃圾 /// </summary> public void DropLitter() { Console.WriteLine("老婆开始打扫房间,线程Id为:{0}", GetThreadId()); Console.WriteLine("垃圾满了,快去扔垃圾"); CommandDropLitter(); Console.WriteLine("不管他继续打扫,线程Id为:{0}", GetThreadId()); Thread.Sleep(100); Console.WriteLine("老婆把房间打扫好了,线程Id为:{0}", GetThreadId()); } /// <summary> /// 通知我去扔垃圾 /// </summary> public async void CommandDropLitter() { Console.WriteLine("这时我准备去扔垃圾,线程Id为:{0}", GetThreadId()); await Task.Run(() => { Console.WriteLine("屁颠屁颠的去扔垃圾,线程Id为:{0}", GetThreadId()); Thread.Sleep(1000); }); Console.WriteLine("垃圾扔了还有啥吩咐,线程Id为:{0}", GetThreadId()); } 运行以上代码: 以上代码在 CommandDropLitter() 方法上加了 async 修饰符,并且使用 await 运算符开启了一个新的 Task 去执行另一个任务。注意:当前线程遇到 await 时,则立刻跳回调用方法继续往下执行。而 Task 执行完成之后将执行 await 之后的代码,并且与 await 之前的线程不是同一个。 2.模拟打开电源开关(关心是否执行完成,返回 Task 类型) /// <summary> /// 打开电源开关 /// </summary> public void OpenMainsSwitch() { Console.WriteLine("我和老婆正在看电视,线程Id为:{0}", GetThreadId()); Console.WriteLine("突然停电了,快去看下是不是跳闸了"); Task task = CommandOpenMainsSwitch(); Console.WriteLine("没电了先玩会儿手机吧,线程Id为:{0}", GetThreadId()); Thread.Sleep(100); Console.WriteLine("手机也没电了只等电源打开,线程Id为:{0}", GetThreadId()); //task.Wait(); //所以这里将被阻塞,直到任务完成 //或者 while (!task.IsCompleted) { Thread.Sleep(100); } Console.WriteLine("又有电了我们继续看电视,线程Id为:{0}", GetThreadId()); } /// <summary> /// 通知我去打开电源开关 /// </summary> public async Task CommandOpenMainsSwitch() { Console.WriteLine("这时我准备去打开电源开关,线程Id为:{0}", GetThreadId()); await Task.Run(() => { Console.WriteLine("屁颠屁颠的去打开电源开关,线程Id为:{0}", GetThreadId()); Thread.Sleep(1000); }); Console.WriteLine("电源开关打开了,线程Id为:{0}", GetThreadId()); } 运行以上代码: 1) 可见,调用 Wait() 方法后,当前线程被阻塞了,直到 Task 执行完成后,当前线程才继续执行。 3. 模拟去买盐(不止关心是否执行完成,还要获取执行结果。返回 Task<TResult> 类型) /// <summary> /// 做饭 /// </summary> public void CookDinner() { Console.WriteLine("老婆开始做饭,线程Id为:{0}", GetThreadId()); Console.WriteLine("哎呀,没盐了"); Task<string> task = CommandBuySalt(); Console.WriteLine("不管他继续炒菜,线程Id为:{0}", GetThreadId()); Thread.Sleep(100); string result = task.Result; //必须要用盐了,等我把盐回来(停止炒菜(阻塞线程)) Console.WriteLine("用了盐炒的菜就是好吃【{0}】,线程Id为:{1}", result, GetThreadId()); Console.WriteLine("老婆把饭做好了,线程Id为:{0}", GetThreadId()); } /// <summary> /// 通知我去买盐 /// </summary> public async Task<string> CommandBuySalt() { Console.WriteLine("这时我准备去买盐了,线程Id为:{0}", GetThreadId()); string result = await Task.Run(() => { Console.WriteLine("屁颠屁颠的去买盐,线程Id为:{0}", GetThreadId()); Thread.Sleep(1000); return "盐买回来了,顺便我还买了一包烟"; }); Console.WriteLine("{0},线程Id为:{1}", result, GetThreadId()); return result; } 运行以上代码: 1) 以上代码 task.Result 会阻塞当前线程,与 task.Wait() 类似。 其他示例 public void AsyncTest() { Console.WriteLine("AsyncTest() 方法开始执行,线程Id为:{0}", GetThreadId()); Task task = Test1(); Console.WriteLine("AsyncTest() 方法继续执行,线程Id为:{0}", GetThreadId()); task.Wait(); Console.WriteLine("AsyncTest() 方法结束执行,线程Id为:{0}", GetThreadId()); } public async Task Test1() { Console.WriteLine("Test1() 方法开始执行,线程Id为:{0}", GetThreadId()); await Task.Factory.StartNew((state) => { Console.WriteLine("Test1() 方法中的 {0} 开始执行,线程Id为:{1}", state, GetThreadId()); Thread.Sleep(1000); Console.WriteLine("Test1() 方法中的 {0} 结束执行,线程Id为:{1}", state, GetThreadId()); }, "task1"); await Task.Factory.StartNew((state) => { Console.WriteLine("Test1() 方法中的 {0} 开始执行,线程Id为:{1}", state, GetThreadId()); Thread.Sleep(3000); Console.WriteLine("Test1() 方法中的 {0} 结束执行,线程Id为:{1}", state, GetThreadId()); }, "task2"); Console.WriteLine("Test1() 方法结束执行,线程Id为:{0}", GetThreadId()); } 运行以上代码: 当异步方法中有多个 await 时,会依次执行所有的 Task,只有当所有 Task 执行完成后才表示异步方法执行完成,当前线程才得以执行。 2. 同样以前面(模拟去买盐)的示例,如果发现其实家里还有盐,这是就要告诉我不用买了(取消异步操作),怎么实现?这就要借助 System.Threading.CancellationTokenSource 和 System.Threading.Tasks.CancellationToken 对象来完成。 /// <summary> /// 做饭(买盐任务取消) /// </summary> public void CookDinner_CancelBuySalt() { Console.WriteLine("老婆开始做饭,线程Id为:{0}", GetThreadId()); Console.WriteLine("哎呀,没盐了"); CancellationTokenSource source = new CancellationTokenSource(); Task<string> task = CommandBuySalt_CancelBuySalt(source.Token); Console.WriteLine("不管他继续炒菜,线程Id为:{0}", GetThreadId()); Thread.Sleep(100); string result = "家里的盐"; if (!string.IsNullOrEmpty(result)) { source.Cancel(); //传达取消请求 Console.WriteLine("家里还有盐不用买啦,线程Id为:{0}", GetThreadId()); } else { //如果已取消就不能再获得结果了(否则将抛出 System.Threading.Tasks.TaskCanceledException 异常) //你都叫我不要买了,我拿什么给你? result = task.Result; } Console.WriteLine("既然有盐我就继续炒菜【{0}】,线程Id为:{1}", result, GetThreadId()); Console.WriteLine("老婆把饭做好了,线程Id为:{0}", GetThreadId()); Console.WriteLine("最终的任务状态是:{0},已完成:{1},已取消:{2},已失败:{3}", task.Status, task.IsCompleted, task.IsCanceled, task.IsFaulted); } /// <summary> /// 通知我去买盐(又告诉我不用买了) /// </summary> public async Task<string> CommandBuySalt_CancelBuySalt(CancellationToken token) { Console.WriteLine("这时我准备去买盐了,线程Id为:{0}", GetThreadId()); //已开始执行的任务不能被取消 string result = await Task.Run(() => { Console.WriteLine("屁颠屁颠的去买盐,线程Id为:{0}", GetThreadId()); Thread.Sleep(1000); }, token).ContinueWith((t) => //若没有取消就继续执行 { Console.WriteLine("盐已经买好了,线程Id为:{0}", GetThreadId()); Thread.Sleep(1000); return "盐买回来了,顺便我还买了一包烟"; }, token); Console.WriteLine("{0},线程Id为:{1}", result, GetThreadId()); return result; } 运行以上代码: if (cancellationToken.IsCancellationRequested) { // Fast path for an already-canceled cancellationToken this.InternalCancel(false); } 3. 乘热打铁,我们再来看看多个 CancellationTokenSource 取消异步任务,以及注册取消后的回调委托方法,继续以(模拟去买盐)为例: /// <summary> /// 做饭(多个消息传达买盐任务取消) /// </summary> public void CookDinner_MultiCancelBuySalt() { Console.WriteLine("老婆开始做饭,线程Id为:{0}", GetThreadId()); Console.WriteLine("哎呀,没盐了"); CancellationTokenSource source1 = new CancellationTokenSource(); //因为存在而取消 CancellationTokenSource source2 = new CancellationTokenSource(); //因为放弃而取消 CancellationTokenSource source = CancellationTokenSource.CreateLinkedTokenSource(source1.Token, source2.Token); //注册取消时的回调委托 source1.Token.Register(() => { Console.WriteLine("这是因为{0}所以取消,线程Id为:{1}", "家里还有盐", GetThreadId()); }); source2.Token.Register((state) => { Console.WriteLine("这是因为{0}所以取消,线程Id为:{1}", state, GetThreadId()); }, "不做了出去吃"); source.Token.Register((state) => { Console.WriteLine("这是因为{0}所以取消,线程Id为:{1}", state, GetThreadId()); }, "没理由"); //这里必须传递 CancellationTokenSource.CreateLinkedTokenSource() 方法返回的 Token 对象 Task<string> task = CommandBuySalt_MultiCancelBuySalt(source.Token); Console.WriteLine("等等,好像不用买了,线程Id为:{0}", GetThreadId()); Thread.Sleep(100); string[] results = new string[] { "家里的盐", "不做了出去吃", "没理由" }; Random r = new Random(); switch (r.Next(1, 4)) { case 1: source1.Cancel(); //传达取消请求(家里有盐) //source1.CancelAfter(3000); //3s后才调用取消的回调方法 Console.WriteLine("既然有盐我就继续炒菜【{0}】,线程Id为:{1}", results[0], GetThreadId()); break; case 2: source2.Cancel(); //传达取消请求(不做了出去吃) //source2.CancelAfter(3000); //3s后才调用取消的回调方法 Console.WriteLine("我们出去吃不用买啦【{0}】,线程Id为:{1}", results[1], GetThreadId()); break; case 3: source.Cancel(); //传达取消请求(没理由) //source.CancelAfter(3000); //3s后才调用取消的回调方法 Console.WriteLine("没理由就是不用买啦【{0}】,线程Id为:{1}", results[2], GetThreadId()); break; } Console.WriteLine("最终的任务状态是:{0},已完成:{1},已取消:{2},已失败:{3}", task.Status, task.IsCompleted, task.IsCanceled, task.IsFaulted); } /// <summary> /// 通知我去买盐(又告诉我不用买了,各种理由) /// </summary> public async Task<string> CommandBuySalt_MultiCancelBuySalt(CancellationToken token) { Console.WriteLine("这时我准备去买盐了,线程Id为:{0}", GetThreadId()); //已开始执行的任务不能被取消 string result = await Task.Run(() => { Console.WriteLine("屁颠屁颠的去买盐,线程Id为:{0}", GetThreadId()); Thread.Sleep(1000); }, token).ContinueWith((t) => //若没有取消就继续执行 { Console.WriteLine("盐已经买好了,线程Id为:{0}", GetThreadId()); Thread.Sleep(1000); return "盐买回来了,顺便我还买了一包烟"; }, token); Console.WriteLine("{0},线程Id为:{1}", result, GetThreadId()); return result; } 运行以上代码: 1) 当调用 source.Cancel() 方法后,会立即取消并调用 token 注册的回调方法;而调用 existSource.CancelAfter() 方法则会等到达指定的毫秒数后才会取消。 前言 什么是异步方法 3. Task 分为两种: 4. 异步方法的返回类型必须为 void、Task、Task<TResult> 中的其中一种。 归纳一下:void 不关心结果;Task 只关心是否执行完成;Task<TResult> 不止关心是否执行完成,还要获取执行结果。 全部评论
专题导读
热门推荐
热门话题
阅读排行榜
|
请发表评论