线程(Thread、ThreadPool)

线程的定义我想大家都有所了解,这里我就不再复述了。我这里主要介绍.NET Framework中的线程(Thread、ThreadPool)。

.NET Framework中的线程分为两类:1.前台线程;2.后台线程。

1.前台线程

class Program
{static void Main(string[] args){Console.WriteLine("=====Thread=====");TestThread();Console.WriteLine("主线程执行完毕");   }public static void TestThread(){Thread thread = new Thread(PrintNum);thread.Start();}public static void PrintNum(){Thread.Sleep(3000);for (int i = 0; i < 10; i++)Console.WriteLine(i);}
}

运行结果

从运行结果可以看出,主线程虽然执行完毕了,但是并没有退出程序,而是等待子线程执行完毕后,退出程序。

2.后台线程

class Program
{static void Main(string[] args){Console.WriteLine("=====ThreadPool=====");ThreadPool.QueueUserWorkItem(new WaitCallback(PrintNum));Console.WriteLine("主线程执行完毕");     }public static void PrintNum(object obj){Thread.Sleep(3000);for (int i = 0; i < 10; i++)Console.WriteLine(i);}
}

运行结果

从运行结果可以看出,主线程运行完毕后,就直接退出了程序,没有等待子线程。

总结:

1.前台线程:主线程执行完毕后,会等待所有子线程执行完毕后,才退出程序。

2.后台线程:主线程执行完毕后,直接退出程序,不论子线程是否执行完毕。

3.推荐:多线程的操作,推荐使用线程池线程而非新建线程。因为就算只是单纯的新建一个线程,这个线程什么事情也不做,都大约需要1M的内存空间来存储执行上下文数据结构,并且线程的创建与回收也需要消耗资源,耗费时间。而线程池的优势在于线程池中的线程是根据需要创建与销毁,是最优的存在。但是这也有个问题,那就是线程池线程都是后台线程,主线程执行完毕后,不会等待后台线程而直接结束程序。所以下面就要引出.NET Framework4.0提供的Task,来解决此类问题。

Task

Task是.NET Framework4.0提供的新的操作线程池线程的封装类。它提供:等待、终止、返回值...优化线程操作的功能。

1.定义

Task 对象是一种的中心思想 基于任务的异步编程模式 首次引入.NET Framework 4 中。 因为由执行工作 Task 对象通常上异步执行一个线程池线程而不是以同步方式在主应用程序线程中,您可以使用 Status 属性,以及 IsCanceled, ,IsCompleted, ,和 IsFaulted 属性,以此来确定任务的状态。

以上MSDN中对Task的定义,从“异步执行一个线程池线程”可以得出Task的后台实现是通过线程池线程实现。

2.补充

Task的性能要优于ThreadPool。

1)ThreadPool的代码将以先进先出的算法存储在全局队列中,并且多个工作者线程之间竞争同一个同步锁。(这就Task性能优于ThreadPool的第一个原因)

2)Task的代码将以先进后出的算法存储在本地队列中,工作者线程执行本地队列中的代码没有同步锁的限制(这是Task性能优于ThreadPool的第二个原因),并且当工作者线程2空闲并且工作者线程1忙碌时,工作者线程2会尝试从工作者线程1(或者别的忙碌的工作者线程)的本地队列尾部“偷”任务,并会获取一个同步锁,不过这种行为很少发生。

3)简单调用

class Program
{static void Main(string[] args){TestSimpleTask();}public static void TestSimpleTask(){Console.WriteLine("=====Task=====");//直接创建Task task2 = new Task(() => {Thread.Sleep(3000);for (int i = 0; i < 10; i++){Console.WriteLine(i);}});//如果你想测试超时等待后,任务是否会继续执行。就替换下面的代码
        task2.Start();task2.Wait();Console.WriteLine("主程序执行完毕");/*测试超时task2.Start();task2.Wait(1000);Console.WriteLine("主程序执行完毕");Console.ReadLine();*/}
}

task.Wati(时间);这个方法可以确定等待任务的执行时间,当超过规定的时间后将不再等待,直接运行之后的代码,但是任务的代码仍然在后台运行,如果想超过等待时间就停止任务的执行,你需要看下文深入学习。(这个方法提供了极大的方便。如果在.NET Framework2.0中,实现类似的功能,需要定义一个时间全局变量,然后在线程中不断的循环判断。)

3.深入学习

Task(Action, CancellationToken, TaskCreationOptions)

以上是MSDN中Task(不包含输入参数与返回值)最复杂的构造函数,包含2个重要的参数CancellationToken、TaskCreationOptions,下面将详细介绍CancellationToken、TaskCreationOptions的意义以及运用。

a.CancellationToken(取消标记)

该类用来检测任务是否被取消。需要与System.Threading.CancellationTokenSource配合使用,CancellationTokenSource主动停止任务。(CancellationToken虽然有检测任务是否停止的属性,但是一旦CancellationTokenSource调用了Cancel()方法,那么任务将立即停止运行,也就是说任务中的任何代码都不会被执行)

场景:主线程抛出异常,在异常处理中停止任务的执行。

class Program
{static void Main(string[] args){TestCancellationTokenTask();}public static void TestCancellationTokenTask(){CancellationTokenSource cts = new CancellationTokenSource();try{Task task = Task.Factory.StartNew(() =>{for (int i = 0; i < 10; i++){//当任务取消时,这段检测代码将永远不会被执行,因为任务已经被取消了if (cts.Token.IsCancellationRequested){Console.WriteLine("=====Task=====");Console.WriteLine("任务被取消");break;}else{Console.WriteLine("=====Task=====");Console.WriteLine("子线程打印:{0}", i);Thread.Sleep(1000);}}}, cts.Token);for (int i = 0; i < 5; i++){if (i == 3){Console.WriteLine("=====Main=====");Console.WriteLine("主线程抛出异常");throw new Exception("测试");}Console.WriteLine("=====Main=====");Console.WriteLine("主线程打印:{0}", i);Thread.Sleep(1000);}task.Wait();}catch{cts.Cancel();}Console.WriteLine(cts.IsCancellationRequested);}
}

注意:主线程抛出异常,无论任务是否被显示取消,都会停止运行。

b.TaskCreationOptions(任务创建选项)

以下是MSDN中关于TaskCreationOptions的枚举值,具体的运用还是要根据实际情况。下面介绍一下AttachedToParent的用法(第5、第6,实际的运用还需要多参考大神的运用)

// 默认
1.None // 将任务放入全局队列中(任务将以先到先出的原则被执行)
2.PreferFairness// 告诉TaskScheduler,线程可能要“长时间运行”
3.LongRunning// 将一个Task与它的父Task关联
4.AttachedToParent// Task以分离的子任务执行
5.DenyChildAttach// 创建任务的执行操作将被视为TaskScheduler.Default默认计划程序
6.HideScheduler// 强制异步执行添加到当前任务的延续任务
7.RunContinuationsAsynchronously场景:将多个任务关联为父子任务

class Program
{static void Main(string[] args){TestTaskCreationOptionsTask();}public static void TestTaskCreationOptionsTask(){StringBuilder sb = new StringBuilder();Task parent = new Task(() =>{new Task(() => {sb.Append("任务1");}).Start();new Task(() => {sb.Append("任务2");}).Start();new Task(() => {sb.Append("任务3");}).Start();//parent任务的调用线程停止5s,这样“任务1”、“任务2”、“任务3”就有时间执行完毕了。//这里用来测试,当任务彼此之间是独立的,那么只有这种方式,控制台才会有打印。//Thread.Sleep(5000);
        });/*Task parent = new Task(() =>{new Task(() => {sb.Append("任务1");}, TaskCreationOptions.AttachedToParent).Start();new Task(() => {Thread.Sleep(3000);sb.Append("任务2");}, TaskCreationOptions.AttachedToParent).Start();new Task(() => {Thread.Sleep(3000);sb.Append("任务3");}, TaskCreationOptions.AttachedToParent).Start();});*/parent.Start();parent.Wait();Console.WriteLine(sb.ToString());}
}

说明:

1.Task的创建如果没有TaskCreationOptions.AttachedToParent,那么任务彼此之间是独立的,parent任务不会等待“任务1”、“任务2”、“任务3”都执行完毕后,才认为已经结束。

2.Task的创建如果有TaskCreationOptions.AttachedToParent,那么父任务必须等待子任务都执行完毕后,才会认为任务结束。

注意:虽然“任务1”、“任务2”、“任务3”是在parent任务中创建,但是可以分配在不同的线程池本地队列中,由不同的线程调用并且任务间并不是串行执行,而是并行执行。

c.返回值

以上介绍的所有内容,Task都没有返回值,但是在实际运用中,Task执行完之后,返回一个值供外部代码使用,这种情况很常见。

class Program
{static void Main(string[] args){TestReturnValueTask();}public static void TestReturnValueTask(){Task<int> task = new Task<int>(num => {Thread.Sleep(5000);return (int)num + 1;}, 100);task.Start();Console.WriteLine(task.Result);}
}

注意:当Task的返回值被调用时,主线程会等待Task执行完毕,才会退出程序。所以这里没有调用task.Wait();

d.TaskContinuationOptions(任务延续选项)

有时候,我们需要在一个任务结束后,执行另一个任务。两个任务之间有先后顺序,这个时候,就需要用到TaskContinuationOptions。

以下是MSDN中关于TaskContinuationOptions的枚举值,这里只列出了部分(4.5新增的枚举只根据MSDN的机器翻译确实不太理解如何运用,还是需要花些时间测试,这里就不列出了,怕误导读者)

// 默认
1.None// 将任务放入全局队列中(任务将以先到先出的原则被执行)
2.PreferFairness// 告诉TaskScheduler,线程可能要“长时间运行”,需要为任务创建一个专用线程,而不是排队让线程池线程来处理
3.LongRunning// 将一个Task与它的父Task关联
4.AttachedToParent// 希望执行第一个Task的线程,执行ContinueWith任务
5.ExecuteSynchronously// 第一个任务没有完成,执行后续任务
6.NotOnRanToCompletion// 第一个任务没有失败,执行后续任务
7.NotOnFaulted// 第一个任务没有取消,执行后续任务
8.NotOnCanceled// 只有当第一个任务取消,执行后续任务
9.OnlyOnCanceled// 只有当第一个任务失败,执行后续任务
10.OnlyOnFaulted// 只有当第一个任务完成,执行后续任务
11.OnlyOnRanToCompletion

class Program
{static void Main(string[] args){TestContinueTask();}public static void TestContinueTask(){/*Task<int> task = new Task<int>(num =>{return (int)num + 1;}, 100);Task taskContinue = task.ContinueWith(c =>{Console.WriteLine(c.Result);}, TaskContinuationOptions.OnlyOnRanToCompletion);task.Start();taskContinue.Wait();*/CancellationTokenSource cts = new CancellationTokenSource();Task<int> task = new Task<int>(num =>{return (int)num + 1;}, 100, cts.Token);Task taskContinue = task.ContinueWith(c =>{Console.WriteLine("任务Task被取消了");}, TaskContinuationOptions.OnlyOnCanceled);task.Start();cts.Cancel();taskContinue.Wait();}
}

写到这里,关于Task的介绍已经结束。但是Task的内容还有很多:异常处理(AggregateException)、取消通知委托(CancellationToken的Register方法)这些内容就留给读者自己去学习了。

有时候,可能需要一次性创建多个任务,并且这些任务共享相同的状态。那么我们就可以通过任务工厂来创建。

TaskFactory

任务工厂,顾名思义,用来创建任务的工厂。在大多数情况下,不需要实例化一个新 TaskFactory<TResult> 实例。 可以使用静态Task<TResult>.Factory 属性,它返回一个工厂对象,将使用默认值。 然后可以调用其方法来启动新任务或定义任务延续。

代码

class Program
{static void Main(string[] args){TestTaskFactory();}public static void TestTaskFactory(){TaskFactory<DateTime> factory = new TaskFactory<DateTime>();Task<DateTime>[] tasks = new Task<DateTime>[] {factory.StartNew(() => { return DateTime.Now.ToUniversalTime(); }),factory.StartNew(() => {Thread.Sleep(5000);return DateTime.Now.ToUniversalTime(); }),factory.StartNew(() => {return DateTime.Now.ToUniversalTime(); })};StringBuilder sb = new StringBuilder();foreach (Task<DateTime> task in tasks)sb.AppendFormat("{0}\t", task.Result);Console.WriteLine(sb.ToString());}
}

注意:任务可以分配在不同的线程池本地队列中,由不同的线程调用并且任务间并不是串行执行,而是并行执行。

Parallel

并行,让多个线程池线程并行工作。由于是并行执行,所以有一点需要注意:工作项彼此之间必须可以并行执行!

class Program
{static void Main(string[] args){TestParallel();}public static void TestParallel(){Parallel.For(0, 10, i =>{Console.WriteLine(i);});List<int> lists = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };Parallel.ForEach(lists, i =>{Console.WriteLine(i);});}
}

说明:

1.Parallel.For效率高于Parallel.Foreach,所以当For与Foreach都可以时,推荐使用For。

2.上面的代码,运行For时,你可能会发现数字是有顺序的打印出来,给人一种串行执行的错觉,你可以断点调试你的代码,会发现确实有多个线程在运行代码。

3.Parallel.For()、Parallel.Foreach()还有一些重载方法,大家可以结合实际情况使用,这里就不复述了。

感谢大家的耐心阅读。

from:https://www.cnblogs.com/color-wolf/p/4850869.html

线程(Thread,ThreadPool)、Task、Parallel相关推荐

  1. C#并行开发_Thread/ThreadPool, Task/TaskFactory, Parallel

    大家好,本次讨论的是C#中的并行开发,给力吧,随着并行的概念深入,哥也赶上这个潮流了,其实之前讨论C#的异步调用或者C#中BeginInvoke或者Invoke都已经涉及了部分本篇的内容. 参考书目: ...

  2. C#.Net使用线程池(ThreadPool)与专用线程(Thread)

    线程池(ThreadPool)使用起来很简单,但它有一些限制: 1. 线程池中所有线程都是后台线程,如果进程的所有前台线程都结束了,所有的后台线程就会停止.不能把入池的线程改为前台线 程. 2. 不能 ...

  3. C#中的thread和task之Task

    简介 Task是在.NET Framework 4中添加进来的,这是新的namespace:System.Threading.Tasks;它强调的是adding parallelism and con ...

  4. 【转】异步编程系列(Thread、Task、async/await、ajax等)

    序 经过一番努力,我写的异步编程系列也算有头有尾,当然不是说这个系列已经更新完毕,这个头尾只是表示新旧知识点都有简单涉及到,接下去我还会丰富这一系列并且有机会整个小应用(愿景是弄一个开源组件吧,结合s ...

  5. Task/Parallel实现异步多线程

    代码: #region Task 异步多线程,Task是基于ThreadPool实现的{//TestClass testClass = new TestClass();//Action<obje ...

  6. C#多线程编程(1)--线程,线程池和Task

    C#多线程编程(1)--线程,线程池和Task 新开了一个多线程编程系列,该系列主要讲解C#中的多线程编程.    利用多线程的目的有2个: 一是防止UI线程被耗时的程序占用,导致界面卡顿:二是能够利 ...

  7. 使用C++11封装线程池ThreadPool

    2019独角兽企业重金招聘Python工程师标准>>> 读本文之前,请务必阅读: 使用C++11的function/bind组件封装Thread以及回调函数的使用 Linux组件封装 ...

  8. 手撕线程池 ThreadPool

    为了更加方便理解线程池 我们都知道线程池和任务就好比生产者消费者之间的关系也就是如图! 先分析:我们可以将线程池中的线程看作消费者,可以将main(只是方便测试)看作生产者 总体思路: 实现一个阻塞队 ...

  9. .NET 线程 Thread 进程 Process、线程池 pool、Invoke、begininvoke、异步回调、APM、EAP、TPL、aysnc、await

    windows系统是一个多线程的操作系统.一个程序至少有一个进程,一个进程至少有一个线程.进程是线程的容器,一个C#客户端程序开始于一个单独的线程,CLR(公共语言运行库)为该进程创建了一个线程,该线 ...

  10. threadpool的数量_多线程之旅(11)_如何限制系统线程池ThreadPool的最大最小并发数量_SetMaxThreads/SetMinThreads用法...

    ThreadPool有两个设置线程池并发数量的方法,分别是: ThreadPool.SetMinThreads(int workerThreads, int completionPortThreads ...

最新文章

  1. python opencv读取网络图片
  2. 换一个角度再谈一下WF
  3. http://blog.csdn.net/lovejavaydj/article/details/6
  4. hystrix之熔断
  5. CodeForces - 1332D Walk on Matrix(构造)
  6. mysql 后10条_Mysql 保留最新的10条数据
  7. IO 多路复用之poll总结
  8. LintCode 1689. k求和III(递归)
  9. Jupyter notebook应用总结
  10. 递归不行就换动态规划(洛谷P1028题题解,Java语言描述)
  11. 问题总结(一)使用代码调整SharePoint WebPart 视图和列表菜单
  12. JavaScript(Bom编程)
  13. 【平衡小车制作】(三)编码器讲解(超详解)
  14. J9数字论:如何避免踩雷多头陷阱?
  15. 两轮差速移动机器人从A点移动到B点的C++语言代码
  16. Java基础之Java8中Map的compute的使用
  17. 莫提博客 - 简约优雅的个人博客系统
  18. 43.XDMA寄存器详解7-MSI-X Vector Table and PBA寄存器组剖析及MSI-X中断详解
  19. 台式计算机电源怎么看,怎么看电脑电源多少w 查看自己电脑功耗方法 (全文)
  20. 草图大师怎么建模?建模后如何渲染?1skp素材库 平层图

热门文章

  1. Spring Cloud【Finchley】-13 Eureka Server HA高可用 2个/3个节点的搭建及服务注册调用
  2. linux 飞行模拟,为推进2020款微软飞行模拟器开发:微软启动Flight Simulator X Beta测试...
  3. java里 currenttime_java 获取当前时间LocalDateTime currentTimeMillis java.util.Date
  4. MUI+H5手机上传照片 支持多图片上传和拍照上传
  5. 华为p4支持鸿蒙功能吗_华为鸿蒙系统可以用了,支持这四款手机,看看有你的吗?...
  6. (三) LtRecyclerView v2.x (自定义上拉和下拉刷新View)
  7. 一本书学会可视化设计 pdf_「读书」数据之美-一本书学会可视化设计
  8. 13个JavaScript单行式代码
  9. 2020-11-06 Python OpenCV给证件照换底色
  10. python post请求参数错误,爬虫POST请求Json格式不清楚报错原因