hello,咋们又见面啦,通过前面两篇文章的介绍,对task的创建、运行、阻塞、同步、延续操作等都有了很好的认识和使用,结合实际的场景介绍,这样一来在实际的工作中也能够解决很大一部分的关于多线程的业务,但是只有这一些是远远不够的,比如,比如,如果这么一个场景,当开启tsak异步任务后,有某个条件触发,需要终止tsak的执行又该如何实现呢?这一些问题正是我们今天需要交流分享的部分,带着这一些问题,咱们共同进入到今天的主题,谢谢!

在进入主题前,如果你没有阅读前面的两篇文章,欢迎您点击下面地址先阅读一下,这样能够更加连贯的掌握了解今天的内容,谢谢!

  第一篇:聊聊多线程哪一些事儿(task)之 一创建运行与阻塞

  第二篇:聊聊多线程哪一些事儿(task)之 二 延续操作

  第三篇:聊聊多线程哪一些事儿(task)之 三 异步取消和异步方法

Task之任务取消:CancellationTokenSource

关于线程取消,我相信大家在实际工作中都会遇到这样的问题,无论是采用哪一种方式实现异步线程,都会有相应的机制来取消线程操作。本次将同时对Thread的线程取消实现,Tsak的线程取消实现同时通过实例说明。

在我的工作经验中,需要取消异步线程作业的实际使用场景往往是一些异步作业程序,也就是一些周期性的,循环业务操作。比如周期性的数据同步、数据更新等等操作。比如:电商系统常见的一个场景,订单超时取消等等。

为了与前两篇的实例保持一致性,我现在还是以酒店平台的数据同步业务为例:

需求:每周三凌晨3点钟,通过携程提供的酒店分页查询接口,全量同步一次最新的酒店数据。并且能够通过人为的干预来终止数据同步操作。

下面我将分别通过Thread和task两种方式来实现

其一、Thread时代之任务取消

哈哈,实话实话说,在几年前的项目中,我也是采用Thread来实现异步线程的,也会遇到线程的取消的业务场景。我当时的实现方式是,定义一个全局变量,isStopThread(是否终止线程),去过需要取消任务,只需要控制isStopThread的值即可,每一次执行具体的业务时,首先判断一下isStopThread,只有非终止状态才执行具体的业务逻辑。 /// <summary> /// 携程 酒店数据同步作业(Thread) /// </summary>

  1. private static void CtripHoteDataSynchrByThread()
  2. {
  3. // CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
  4. // 第一步通过thread开启一个线程
  5. Thread thread = new Thread(() =>
  6. {
  7. // 获取数据的次数
  8. int getDataIndex = 1;
  9. isStopCtripHoteDataSynchr = false;
  10. // 通过调用携程的分页服务,获取其有效的酒店数据
  11. // 在获取数据前,首先判断一下是否终止获取
  1. // while (!cancellationTokenSource.IsCancellationRequested)
  1. while (!isStopCtripHoteDataSynchr )
    {
  2. // 现在假设模拟,获取携程的所有有效的酒店数据通过 3 次就获取完毕
  3. Console.WriteLine($"开始获取携程第 {getDataIndex} 页酒店数据....\n");
  4. Thread.Sleep(3000);
  5. Console.WriteLine($"携程第 {getDataIndex} 页酒店数据获取完毕\n");
  6. getDataIndex++;
  7. // 模拟获取完第三页数据,代表数据获取完毕,直接终止掉
  8. if (getDataIndex == 4)
  9. {
  10. Console.WriteLine($"同步完毕携程的所有酒店数据\n");
  11. break;
  12. }
  13. }
  14. if (isStopCtripHoteDataSynchr)
  15. {
  16. Console.WriteLine($"取消同步携程酒店数据\n");
  17. }
  18. });
  19. thread.Start();
  20. Console.WriteLine("携程酒店数据同步中.....\n");
  21. // 模拟实际数据同步中的取消操作
  22. Console.WriteLine("如果需要取消数据同步,那么请输入任意字符即可取消操作\n");
  23. Console.ReadLine();
  24. // cancellationTokenSource.Cancel();
  25. isStopCtripHoteDataSynchr = true;
  26. Console.WriteLine($"发起取消同步携程酒店数据请求\n");
  27. }
  1.  

执行结果:

通过测试结果我们可以看到,在获取第2页数据时,此时发起了一个取消线程命令,当第二页数据获取完毕后,线程就里面终止了,从而到达了线程取消的目的。

其二、Task时代之任务取消

随着Task的推出,微软也推出了一个专门服务于线程取消的帮助类(CancellationTokenSource),通过该类能够很好的帮助我们取消一个线程,话不多说,我们先通过CancellationTokenSource类实现上面示例的功能。

  1. /// <summary>
  2. /// 携程 酒店数据同步作业(Task)
  3. /// </summary>
  4. private static void CtripHoteDataSynchrByTask()
  5. {
  6. // 定义任务取消机制
  7. CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
  8.  
  9. // 第一步通过thread开启一个线程
  10. Task thread = new Task(() =>
  11. {
  12. // 获取数据的次数
  13. int getDataIndex = 1;
  14.  
  15. // 通过调用携程的分页服务,获取其有效的酒店数据
  16. // 在获取数据前,首先判断一下是否终止获取
  17.  
  18. while (!cancellationTokenSource.IsCancellationRequested)
  19. {
  20. // 现在假设模拟,获取携程的所有有效的酒店数据通过 3 次就获取完毕
  21. Console.WriteLine($"开始获取携程第 {getDataIndex} 页酒店数据....\n");
  22.  
  23. Console.WriteLine($"携程第 {getDataIndex} 页酒店数据获取完毕\n");
  24. getDataIndex++;
  25.  
  26. // 模拟获取完第三页数据,代表数据获取完毕,直接终止掉
  27. if (getDataIndex == 4)
  28. {
  29. Console.WriteLine($"同步完毕携程的所有酒店数据\n");
  30. break;
  31. }
  32. }
  33.  
  34. if (cancellationTokenSource.IsCancellationRequested)
  35. {
  36. Console.WriteLine($"取消同步携程酒店数据\n");
  37. }
  38. });
  39. thread.Start();
  40.  
  41. Console.WriteLine("携程酒店数据同步中.....\n");
  42. // 模拟实际数据同步中的取消操作
  43. Console.WriteLine("如果需要取消数据同步,那么请输入任意字符即可取消操作\n");
  44.  
  45. Console.ReadLine();
  46.  
  47. // 直接取消线程
  48. cancellationTokenSource.Cancel();
  49.  
  50. // 在指定时间后取消线程
  51. // cancellationTokenSource.CancelAfter(1000);
  52.  
  53. Console.WriteLine($"发起取消同步携程酒店数据请求\n");
  54. }
  1.  

测试结果:

通过测试结果,两种实现方式的结果完全一致

当然,CancellationTokenSource 还提供了CancelAfter(多久后取消)方法,来实现多久后取消线程。

说到这儿,不知道大家有没有发现一个问题CancellationTokenSource 其实现是不是与Task和Thread没有多少关系,在第一个实例中通过Thread实现的线程取消,同样可以结合CancellationTokenSource 来实现。所以说,在开始我说CancellationTokenSource 是微软提供的一个线程取消的一个帮助类就是这个原因。其实我可以打开CancellationTokenSource 的实现源码,其实我们就会一目了然,其取消线程的核心逻辑和我们上面的说Thread取消的原理很类似,都是控制一个变量的值来实现,只是CancellationTokenSource 对其所有操作进行了一个封装。其中的CancelAfter里面是开启了一个定义器,定时器的最终实现还是和Canel一样。如果想看CancellationTokenSource的源码,大家可以查看下面地址:https://www.cnblogs.com/majiang/p/7920102.html

最后需要说明的是Task与CancellationTokenSource都是.net Framework4.0+、.NET Core、.NET Standard。

异步方法之:(async/await)

c#5.0微软推出了一个新的特性那就是异步方法,其关键词为async。有了async我们要实现一个异步方法就简单的多啦,你会发现和实现一个同步方法很相似,只需要对方法加以async修饰即可。当然如果只是简单的修饰调用,那么也会是同步调用,为了达到真正的异步调用,往往是需要另外一个关键词await来配合使用。

先简单介绍一下async异步函数:

async的三种返回类型:

Tsak:其主要适用场景是,主程序只关心异步方法执行状态,不需要和主线程有任何执行结果数据交互。

Task<T>:其主要适用场景是,主程序不仅仅关心异步方法执行状态,并且还希望执行后返回一个数据类型为T的结果

void: 主程序既不关系异步方法执行状态,也不关心其执行结果,只是主程序调用一次异步方法,对于除事件处理程序以外的代码,通常不鼓励使用 async void 方法,因为调用方不能

  在介绍一下await关键词:

await其顾名思义就是等待的意思,其运行原理就是:调用方执行到await时就会立即返回,但是异步方法等待异步执行结果。所以await只能存在于async修饰的异步方法体中,await不阻塞主线程,只是阻塞当前异步方法继续往下执行,这样就能够达到真正异步的目的。

下面以一个简单的例子来说明一下每一种情况的使用:

  1. static void Main(string[] args)
  2. {
  3. Console.WriteLine("主线程开始\n");
  4. Console.WriteLine("主线程调用同步方法:SynTest\n");
  5. SynTest();
  6.  
  7. Console.WriteLine("主线程调用异步方法:AsyncTestNoAwait\n");
  8. AsyncTestNoAwait();
  9.  
  10. Console.WriteLine("主线程调用异步方法:AsyncTestHasAwait\n");
  11. AsyncTestHasAwait();
  12.  
  13. Console.WriteLine("主线程结束\n");
  14. Console.ReadKey();
  15. }
  16.  
  17. /// <summary>
  18. /// 同步方法测试
  19. /// </summary>
  20. public static void SynTest()
  21. {
  22. Console.WriteLine("同步方法SynTest开始运行\n");
  23. Thread.Sleep(5000);
  24. Console.WriteLine("同步方法SynTest运行结束\n");
  25. }
  26.  
  27. /// <summary>
  28. /// 异步方法测试(不带有 await关键词)
  29. /// </summary>
  30. public static async void AsyncTestNoAwait()
  31. {
  32. Console.WriteLine("异步方法AsyncTestNoAwait开始运行\n");
  33. Thread.Sleep(5000);
  34. Console.WriteLine("异步方法AsyncTestNoAwait运行结束\n");
  35. }
  36.  
  37. /// <summary>
  38. /// 异步方法测试(带有 await关键词)
  39. /// </summary>
  40. public static async void AsyncTestHasAwait()
  41. {
  42. Console.WriteLine("异步方法AsyncTestHasAwait开始运行\n");
  43. await Task.Delay(5000);
  44. Console.WriteLine("异步方法AsyncTestHasAwait运行结束\n");
  45. }

  1. 运行结果:

从运行结果我们可以很好的得出:

1、异步方法async如果没有await关键词,其执行原理还是同步调用

2、await关键词只能存在云async修饰的方法体中

3、异步方法async在调用时,只有遇到await关键词后的程序块才是异步执行,其await关键词前的代码块还是同步执行

好了,管理async先介绍到这儿,由于时间和文章篇幅原因,就不在详细介绍,里面还有很多内容需要注意,后续在根据实际做一个async/await的专题文章。

总结:

到目前为止,有关Task的3篇文章都到此结束,下面在回顾总结一下Task的相关功能点吧!

1、Task的创建运行可以有三种方式:new Task/Task.Factory/Task.Run

2、Task的返回参数定义Task<返回类型>

获取返回值:Task.Result->要阻塞主流程

3、Task线程的同步实现不仅仅可以通过RunSynchronously来实现同步运行,当然还可以通过Task.Result/Task.Wait等方式来变向实现

4、Task的wait/waitAll/waitAny实现阻塞等待执行结果

5、Task的WhenAny、WhenAll、ContinueWith实现延续操作

6、CancellationTokenSource实现异步任务取消

7、异步方法之:(async/await)实现同步和异步调用等

猜您喜欢:

 第一篇:聊聊多线程哪一些事儿(task)之 一创建运行与阻塞

 第二篇:聊聊多线程哪一些事儿(task)之 二 延续操作

END
为了更高的交流,欢迎大家关注我的公众号,扫描下面二维码即可关注,谢谢:

  1. isStopCtripHoteDataSynchr

聊聊多线程那一些事儿(task)之 三 异步取消和异步方法的更多相关文章

  1. 聊聊多线程哪一些事儿(task)之 三 异步取消和异步方法

    hello,咋们又见面啦,通过前面两篇文章的介绍,对task的创建.运行.阻塞.同步.延续操作等都有了很好的认识和使用,结合实际的场景介绍,这样一来在实际的工作中也能够解决很大一部分的关于多线程的业务 ...

  2. 聊聊多线程哪一些事儿(task)之 一

    多线程,一个多么熟悉的词汇,作为一名程序员,我相信无论是从事什么开发语言,都能够轻轻松松说出几种实现多线程的方式,并且在实际工作种也一定用到过多线程,比如:定时器.异步作业等等,如果你说你没有用过多线 ...

  3. 聊聊多线程哪一些事儿(task)之 二 延续操作

    hello,又见面啦,昨天我们简单的介绍了如何去创建和运行一个task.如何实现task的同步执行.如何阻塞等待task集合的执行完毕等待,昨天讲的是task的最基本的知识点,如果你没有看昨天的博客, ...

  4. 聊聊多线程那一些事儿 之 五 async.await深度剖析

     hello task,咱们又见面啦!!是不是觉得很熟读的开场白,哈哈你哟这感觉那就对了,说明你已经阅读过了我总结的前面4篇关于task的文章,谢谢支持!感觉不熟悉的也没有关系,在文章末尾我会列出前四 ...

  5. 聊聊RabbitMQ那一些事儿之一基础应用

    聊聊RabbitMQ那一些事儿之一基础应用 Hi,各位热爱技术的小伙伴您们好,今年的疫情害人啊,真心祝愿您和您的家人大家都平平安安,健健康康.年前到现在一直没有总结点东西,写点东西,不然久了自己感觉自 ...

  6. 异步编程系列06章 以Task为基础的异步模式(TAP)

    p { display: block; margin: 3px 0 0 0; } --> 写在前面 在学异步,有位园友推荐了<async in C#5.0>,没找到中文版,恰巧也想提 ...

  7. SpringBoot整合全局异常处理&SpringBoot整合定时任务Task&SpringBoot整合异步任务

    ============整合全局异常=========== 1.整合web访问的全局异常 如果不做全局异常处理直接访问如果报错,页面会报错500错误,对于界面的显示非常不友好,因此需要做处理. 全局异 ...

  8. C#.NET使用Task,await,async,异步执行控件耗时事件(event),不阻塞UI线程和不跨线程执行UI更新,以及其他方式比较

    使用Task,await,async,异步执行事件(event),不阻塞UI线程和不跨线程执行UI更新 使用Task,await,async 的异步模式 去执行事件(event) 解决不阻塞UI线程和 ...

  9. .Net Core WebAPI 基于Task的同步&异步编程快速入门

    .Net Core WebAPI 基于Task的同步&异步编程快速入门 Task.Result async & await 总结 并行任务(Task)以及基于Task的异步编程(asy ...

随机推荐

  1. IDEA-创建WEB项目并部署Tomcat

    一.创建简单web项目 1.创建一个web project File -> new Project ->选择project sdk 为1.6(如果没有sdk的同学请先配置)-> Ne ...

  2. 一维数组的求平均成绩 Day06

    package com.sxt.arraytest1; /* * 求班里学生的平均成绩,以及成绩的综合 输出每个同学的成绩 */ import java.util.Arrays; import jav ...

  3. python 列表索引

  4. Facebook F8|闲鱼高级技术专家参会分享

    笔者代表闲鱼参加了Facebook在4月30日举行的为期二天的F8大会,地点加州.将会议概括和一些收获分享给大家.对国内开发者而言,Facebook的产品设计.社区.VR/AR等有一些借鉴意义:对海外 ...

  5. android学习——android项目的的目录结构

       然后我们看一下Helloword的程序目录: 我们可以看到 大致有的文件: 1. MainHelloWorld.java文件 2. R.java文件 3. android.jar文件 4. RE ...

  6. sspanel 常用审计规则

    规则 禁用 BT 防止版权争议 BitTorrent protocol 数据包明文匹配 禁止 百度高精度定位 防止IP与客户端地理位置被记录 (api|ps|sv|offnavi|newvector| ...

  7. linux下oracle查询中文乱码

    export NLS_LANG=AMERICAN_AMERICA.UTF8

  8. 5分钟了解为什么学习Go

    1.什么是Go语言? Google开源 编译型语言 21世纪的C语言(主流编程语言都是单线程环境下发布的) 2.Go语言的特点? 简单易学习(类似python学习难度,自带格式化) 开发效率高 执行性 ...

  9. poj 3572 Hanoi Tower

    Hanoi Towers Time Limit : 10000/5000ms (Java/Other) Memory Limit : 131072/65536K (Java/Other) Total ...

  10. BiLSTM-CRF学习笔记(原理和理解) 维特比

    BiLSTM-CRF 被提出用于NER或者词性标注,效果比单纯的CRF或者lstm或者bilstm效果都要好. 根据pytorch官方指南(https://pytorch.org/tutorials/ ...