多线程编程学习笔记——任务并行库(一)
前面我们学习了什么是线程,线程之间的同步,使用线程池。使用线程池可以减少我们大量短时间操作的并行线程所用的操作系统资源。
在net framework 4.0中微软又提供了一个新的异步操作的功能,叫做任务并行库(TPL)。任务并行库的核心是任务(task)。一个任务代表了一个异步操作,譔操作可以通过多种方式运行,可以使用或不使用独立的线程。
一个任务(Task)可以通过多种方式和其他任务组合起来使用。例如,可以同时开启多个任务,等待所有任务完成,再起一个任务进行操作。一个任务可以有多个其他任务组成,这些任务也可以依次拥有自己的子任务。
C#5.0及之后的版本都已经内置了对TPL的支持,允许我们使用await与async关键字进行任务执行。
以下示例,我们使用.Net Framework 4.5之后版本。
一、 创建任务
下面的示例,我们使用task构造函数创建了两个任务。我们传入了一个lambda表达式做为操作任务。然后使用start启动任务。
接着,我们使用task.Run和task.startNew方法来运行两个任务。与使用task构造函数不同之处,在于这两个被创建的任务会立即执行。所以无需显式地调用 这些任务的Start方法。从task1到task4所有任务都是放在线程池中执行的,多次执行,可以发现执行顺序是不一样的。
Task5,由于我们标记为了长时间运行,所以是一个单独的线程,不是线程池中的线程来运行的。
- 代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace ThreadTPLDemo { class Program { static void Main(string[] args) { Console.WriteLine("Task 运行示例 ————{0}",DateTime.Now); var task1 = new Task(() => TaskOper("Task1")); var task2 = new Task(() => TaskOper("Task2")); task1.Start(); task2.Start(); Task.Factory.StartNew(() => TaskOper("Task 3")); Task.Run(() => TaskOper("Task 4")); //长时间运行 Task.Factory.StartNew(() => TaskOper("Task 5"),TaskCreationOptions.LongRunning); Thread.Sleep(1000); Console.ReadKey(); } private static void TaskOper(string name) { Console.WriteLine("Task 运行在 线程 ID:{0} 上,这个线程是不是线程池中的线程:{1},名称: {2}", Thread.CurrentThread.ManagedThreadId,Thread.CurrentThread.IsThreadPoolThread, name); } } }
2.运行结果如下图。我把程序运行了两次。请自行查看不同之处。
二、 使用任务执行基本的操作
本示例是从任务中获取结果值。我们通过不同的执行结果来显示在线程池中执行与在主线程中执行的不同之处。
1. 代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace ThreadTPLDemo { class Program { static void Main(string[] args) { Console.WriteLine("Task 基本操作 ————"); TaskOper("----主线程Task运行"); Task<string> task1 =CreateTask("Task1"); task1.Start(); string result = task1.Result; Console.WriteLine(" 运行结果——{0}", result); Task<string> task2 = CreateTask("Task2"); task2.RunSynchronously(); result = task1.Result; Console.WriteLine(" 运行结果——{0}", result); Task<string> task3 = CreateTask("Task3"); task3.Start(); while(!task3.IsCompleted) { Console.WriteLine(" 状态——{0}", task3.Status); Thread.Sleep(500); } Console.WriteLine(" ——状态—{0}", task3.Status); result = task3.Result; Console.WriteLine(" 运行结果——{0}", result); Console.ReadKey(); } private static string TaskOper(string name) { Console.WriteLine("Task 线程 ID:{0} 上,是不是线程池中的线程:{1},名称: {2}", Thread.CurrentThread.ManagedThreadId,Thread.CurrentThread.IsThreadPoolThread, name); Thread.Sleep(2000); return string.Format("线程ID:{0},名称:{1}", Thread.CurrentThread.ManagedThreadId,name); } static Task<string> CreateTask(string name) { return new Task<string>(() => TaskOper(name)); } } }
2.程序运行结果如下图。
首先直接运行TaskOper方法,根据程序运行结果,我们可以知道这个方法是被同步执行的。
然后我们运行了task1,使用start方法启动任务并等待结果。这个任务会被放在线程池中运行,而且主线程会等待,直到任务结束并返回结果。
Task2与task1相似,Task2通过RunSynchronously()方法运行的。这个任务运行在主线程中,这个任务的输出与TaskOper方法输出结果一样。这就是task的优势,可以使用task对TaskOper方法进行优化,可以避免使用线程池来执行一些执行时间非常短的操作。
Task3运行task1的方法,但是这次没有阻塞主线程,只是在任务完成之前循环打印出任务状态。
相关文章
- 数说,世界杯让你印象最深刻的面孔
- 记一次docker虚拟机横向移动渗透测试
- 华为秋招面经分享!
- 面试官:如何保证接口幂等性?一口气说了12种方法!
- 网易秋招高频面试题汇总
- 《前端图形学实战》几何学在前端边界计算中的应用和原理分析
- 前端图形学实战: 从零实现编辑器的图层管理面板和实时缩略图(vue3 + vite版)
- 结构建模设计——Solidworks软件之装配体操作基本总结一(装配体功能界面简介、插入零件操作、基本配合操作)
- 程序员如何准备好一次面试
- 面试问到DCL失效不知所措
- Kafka大厂高频面试题:在保证高性能、高吞吐的同时保证高可用性
- 域渗透-横向移动命令总结
- PHP程序员面试时经常会被考的冒泡排序算法
- Spring框架学习笔记(2)——面向切面编程AOP
- Jsp学习笔记(2)——页面导航、表单、EL表达式
- ArcGIS QGIS学习二:图层如何只显示需要的部分几何面数据(附最新坐标边界下载全国省市区县乡镇)
- 新开源HTML5单文件网页版ACME客户端,可在线申请Let's Encrypt、ZeroSSL免费HTTPS多域名通配符泛域名SSL/TLS证书(RSA/ECC/ECDSA)
- Java开发桌面程序学习(13)——Javafx多线程 下载功能
- 移动端实现HTML5 mp3录音踩坑指南:系统播放音量变小、一些机型录音断断续续 之 MediaRecorder和AudioWorklet的终极对决
- 最新全国省市区县乡镇街道行政区划数据提取(2022年)