让那些做面试官的屌丝lead不再抖脚系列(四)---线程定义(Thread,Task等),
来源:互联网 发布:ti6双败赛制 知乎 编辑:程序博客网 时间:2024/05/12 17:02
好了扯了那么多死记硬背的,我们需要点活的。
在写实际的之前,我们先了解下异步的历史,当然我们在之后会找一个篇幅来引入我们的.net的发展史,这边先了解下异步。
BeginEndInvoke
其实最原始的异步,大家肯定都用过,所谓的BeginXX/EndXX,详细点就是BeginInvoke()开始调用和EndInvoke阻塞线程,即类似于wait。感觉还是说的太专业了,很简单的例子:
1. BeginInvoke()....
2
3
4
5.EndInvoke()....
6.
执行顺序就是1一边在跑,然后2 3 4也一同在跑,直到跑到5,开始等待begin中的跑完,然后再继续跑6。
当然同步异步带来的就是一个叫callback,即回调函数的东西,就是当异步方法跑完后,还要跑一个方法表示,欧,我结束了,比如我们需要异步获取所有员工的信息,然后在获取完了之后计算总开销,那这个计算方法就是要在获取员工信息的回掉函数中执行,其实很简单的理论,写起来也很简单。
然后是原始.net中begin/endinvoke的写法:
private delegate int SuperDelegate(string para); private static int SuperMethod(string para) { return System.Convert.ToInt16(para); } private static void SuperCallBack(IAsyncResult result) { var returnValue = ((SuperDelegate)result.AsyncState).EndInvoke(result); }
调用:
SuperDelegate superDelegate = SuperMethod; superDelegate.BeginInvoke("1", SuperCallBack, superDelegate);
callback中的returnValue就是我们method的返回值了,说到endInvoke会阻塞线程,其实道理是,谁调用我就塞住谁,所以在callback中调用相当于还是在实行我们method方法的线程中调用,因此主线程不会被阻塞了。
人的想法是无限的,对于代码的美观性更是无限的,所以有了lamda表达式,而对于delegate,用lamda的结果就会变成这样:
SuperDelegate superDelegate = (s) => System.Convert.ToInt16(s); superDelegate.BeginInvoke("1", (result) => { var returnValue = superDelegate.EndInvoke(result); }, superDelegate);
看不懂lamda的童鞋我们之后会特别开一次课让大家明白=>的世界和普通世界是多么不一样。
然后是我们的Task,Thread,这个在现在的版本中反而不多见了,但是这个感觉像是把begin,end封装了的东西,还是有他的好处的。
这两个都是.net4.0才有的产物,先来介绍下task吧,其实也没什么好介绍的。
var task1 = SyncTask.Task.Factory.StartNew(() => { });
这里只写了factory模式了,另一个new了再start的就不写了,其实就是很简单的开一个线程,new一个task对象,然后,就根本停不下来的new。
而thread中和task拥有一样的模式,也是一个普通的new的方法,和 ThreadPool.QueueUserWorkItem((callback) => { }); 线程池分配的模式,类似于factory的模式。
那么问题来了,task和thread或者threadpool有神马区别呢。
首先按效率上来说,
这个是从有关部门转载的,可以看到threadpool的方式明显比其他的快,
想要拿到threadpool的参数,我们虽然可以用:
int aa=0; ThreadPool.QueueUserWorkItem((callback) => aa = 1 + 2);
这样的,但是其实就是一个代理里再加你想要的方法,而且因为是异步进行,所以无法拿aa做些什么,因为你都不知道他什么时候变成3。
所以先从ThreadPool切入,我们按照强行中断,即取消线程,回调函数,还有扩展,这三方面开始对每个模式进行讨论:
ThreadPool
-> 取消线程:原生是不支持中断的,所以聪明的人类想出了个方法:通过CancellationTokenSource实例,来介入线程干你想干的事,但是负面效果就是,你必须带着他玩:
private static int CountValue(CancellationTokenSource token , int add1,int add2) { if (token.IsCancellationRequested) { return 99; } return add1 + add2; }
这是我们自定义的方法,然后调用:
int aa=0; CancellationTokenSource cts = new CancellationTokenSource(); ThreadPool.QueueUserWorkItem((callback) => aa = CountValue(cts,1,2)); cts.Cancel();
所以我们的cts其实就是个监控装置,虽然看起来挺好,但是给方法定义带来了不少麻烦
值得一提的有两点,第一就是线程池木有回调函数的机制可以设置,第二就是,QueueUserWorkItem()的第二个参数,可以设置为前面方法的参数,即类似于Task<>的方式传参,因此这个方法可传参,但是不能接返回值,因此回掉函数也变得意义渐淡。
这里要详细介绍下线程池的概念,首先当然就是ThreadPool和new个Thread的差别,
其实线程池中本来线程就已经安排好,我们可以通过ThreadPool.GetAvailableThreads()获取可用线程数,当然也可以Get/SetMaxThreads()来设置最大线程数,而Thread就是你要一个new一个,结束了就销毁,但是线程池中的线程,完成了任务后就会自动回到池子中,成为挂起的空闲线程待用。因此在性能损耗上,线程池有很大优势。
CLR线程池并不会在CLR初始化时立即建立线程,而是在应用程序要创建线程来运行任务时,线程池才初始化一个线程。
这句话为我们解释了线程池的由来。而对于线程管理,除了我们上面提到的,还有ManualResetEvent这个类,
1、创建一个ManualResetEvent的对象,就像一个信号灯,指示线程的挂起和执行;
2、ManualResetEvent对象创建时,可以指定默认状态:true为有信号,false为无信号;
3、调用Reset()方法重置状态;
4、调用WaitOne()方法,使线程处于等待状态;
5、调用Set()方法设置状态。
这个是转载的,也是很明确,其实threadPool在我眼里,就是个完全靠别人的东西,他自己生活无法自理,需要别的对象来帮助他控制和管理线程。http://www.cnblogs.com/qingyun163/archive/2013/01/05/2846633.html
这个帖子吧ManualResetEvent的原理描述的很清楚,WaitOne是等待Set,而Reset是重置。
Task<>
这个大家都比较熟悉了,这个比起Thread的优势就是他可以接受返回值
var task1 = SyncTask.Task.Factory.StartNew<string>(() => { return "aa"; }); var rea = task1.Result;
而task另一个优势就在于他可以通过ContinuedWith()方法持续执行别的方法。
当然,我们依旧可以通过CancellationTokenSource来控制线程:
CancellationTokenSource cts = new CancellationTokenSource(); var task1 = SyncTask.Task.Factory.StartNew<string>((s) => { return "aa"; }, cts); cts.Cancel();
同时我们还有这么个神奇的东西:
CancellationTokenSource cts1 = new CancellationTokenSource(); CancellationTokenSource cts2 = new CancellationTokenSource(); CancellationTokenSource cts3 = new CancellationTokenSource(); CancellationTokenSource cts = CancellationTokenSource.CreateLinkedTokenSource(cts1.Token, cts2.Token, cts3.Token); var task1 = SyncTask.Task.Factory.StartNew<string>((s) => { return "aa"; }, cts.Token); ct1.Cancel();
当cts1结束时,task1会被取消,但是cts.Token会被取消,也就是相当于调用了cts.Cancel(),但是,cts2,cts3是不取消的,这点要注意了。
由于篇幅问题,这节我们先到这里,下一节我们将继续Task的深究
- 让那些做面试官的屌丝lead不再抖脚系列(四)---线程定义(Thread,Task等),
- 让那些做面试官的屌丝lead不再抖脚系列(五)--- UI线程到底是什么
- 让那些做面试官的屌丝lead不再抖脚系列(六)--- 众望所归的async/await
- 让那些做面试官的屌丝lead不再抖脚系列(三)---同步异步探讨->lock
- 让那些做面试官的屌丝lead不再抖脚系列(二)---同步异步探讨->线程的一些方法
- 让那些做面试官的屌丝lead不再抖脚系列(一)---同步异步探讨->进程和线程
- Thread--Syn系列(四)线程构造方式
- 做一个Lead要那些技能?
- C#中 Thread,Task,Async/Await,IAsyncResult 的那些事儿!
- C#中 Thread,Task,Async/Await,IAsyncResult 的那些事儿!
- Thread (四)线程让步
- 面试系列(四)
- Java实习面试的那些事(四)
- **JAVA** Thread系列四 多线程的应用
- Thread详解四(线程池)
- Thread系列之ManualResetEvent(四)
- Android task process thread 进程与线程
- Java线程系列(1)——thread dump格式、锁与线程的状态
- Linux-千兆网卡驱动实现机制浅析 原文地址:http://blog.csdn.net/fengying765/article/details/6776394
- python 接收邮件(下载附件,解决中文乱码)自测OK
- 求阙斋记
- js - form提交action之前用js操作
- 白话MyCat——MyCat部署运行(Linux环境)与使用步骤详解
- 让那些做面试官的屌丝lead不再抖脚系列(四)---线程定义(Thread,Task等),
- Centos查看用户uid,修改用户主目录
- EditText 键盘收回
- Android读取响应,并转换为字符
- nyoj 136
- array_chunk()和array_splice()函数介绍
- 数据结构与算法之----串
- 图论题库
- 在非UI线程中调用ProgressBar的setProgress函数,刷新进度条