从Thread,ThreadPool,Task, 到async await 的基本使用方法解读
2017-06-02 18:20
846 查看
记得很久以前的一个面试场景:
面试官:说说你对JavaScript闭包的理解吧?
我:嗯,平时都是前端工程师在写JS,我们一般只管写后端代码。
面试官:你是后端程序员啊,好吧,那问问你多线程编程的问题吧。
我:一般没用到多线程。
面试官:............................. (面试结束)
好了,哈哈一笑后,我们来看看 Thread,ThreadPool,Task, async, await 的使用解读。
执行结果:
上面的代码,大家应该都很好理解,通过new Thread 来创建一个子线程,然后.Start() 开始执行。
我们F12 ThreadStart 看到 public delegate void ThreadStart(); 是一个无参数无返回值的委托,那么,如果要在线程中执行一个有参数的方法怎么办了?
OK,我们看Thread的构造函数
ParameterizedThreadStart 是什么?按字面上意思就是带参数的ThreadStart,继续F12看看它
果然是可以带一个object的参数。
改造一下刚才的代码:
执行结果:
(当然还可以用ThreadStart(()=>{ }) 直接用lambda表达式的方式,这里就不写示例代码了 )
看到上面的执行结果,子线程因为Thread.Sleep(100) ,所以每次都最后才打印出输出结果,那么你可能会疑问,如果我想等子线程执行完,我再执行主线程后面的代码,怎么办?
注意看, 加了这句 thread.Join() ,管他什么意思,我们先看看执行结果吧!
OK,是不是明白Join()的意义了?
如果你的代码设计了大量使用Thread,那么有可能会超过系统最大的线程数导致崩溃,而且每次创建和销毁线程也是很耗资源,ThreadPool就可以帮你提高代码效率并管理你的线程。
这不是重点,今天重点是学习它的基础使用方法。
先看看WaitCallback的定义
一个带参数的委托,这就要求它的委托方法必须带一个object的参数了。
ThreadPool静态类通过QueueUserWorkItem()方法将工作函数排入线程池,它不需要我们主动的.Start(),那么他能不能Join()了?
我们点一下就知道了,它既不要你手动Start也没有Join这样的方法。
好了,简单学习Thread和ThreadPool后,发现他们构造函数中都是没有返回值的委托,如果我们要在主线程中获取子线程执行方法的返回值,怎么办? Task闪亮登场了!
Task 的三种创建线程的方法,Task.Run() 不需要手动Start() 。其他两种方式是需要手动Start()。 他们没有Join()方法,取而代之的是Wait()
我们用Run()方法为例,看Task如何获取方法的返回值。
执行结果:
通过task.Result 我们获取到了在子线程中ThreadTest方法的返回值。有没有注意,主线程是等子线程执行完之后才打印最后输出的! task.Result 除了拿到返回值外,是不是和Wait()类似?
看到这里,你肯定会想到,这样另起线程去跑耗时作业和我们平时普通写法有什么区别?效率上会高很多吗?我们来测试看看!
常规方法:
Task方法:
这就很尴尬了,用Task反而执行时间更长!!! 是不是我的打开方式不对?
说了大半天,估计你最期待的async await 还没见身影! 先放下上面的疑问,喝口水,我们来看看今天的主角async await!
await必须是在async修饰的异步方法体内,await后面必须是一个异步方法或者Task。表示异步等待后面方法的结果。
1.返回void的使用方法
执行结果
2.返回Task<T>的使用方法
返回的是Task<T>,那么像得到它的返回值,肯定也是通过.Result了,我们肯定有疑问了,这样和直接写Task有什么区别? 只是为了更加方便和美观吗?
接下来我们来测试下执行效率!
我的天,这效率 !!!!
但有个问题一直没有想通, 如果这样写 (var result = ThreadTest().Result;) 那么耗时和之前的差不多, why? 召唤大神!!!! 跪求解释!
面试官:说说你对JavaScript闭包的理解吧?
我:嗯,平时都是前端工程师在写JS,我们一般只管写后端代码。
面试官:你是后端程序员啊,好吧,那问问你多线程编程的问题吧。
我:一般没用到多线程。
面试官:............................. (面试结束)
好了,哈哈一笑后,我们来看看 Thread,ThreadPool,Task, async, await 的使用解读。
1.Thread
private static void Main(string[] args) { System.Console.WriteLine("主线程开始"); var thread = new Thread(new ThreadStart(ThreadTest)); thread.Start(); System.Console.WriteLine("主线程结束"); System.Console.ReadLine(); } private static void ThreadTest() { System.Console.WriteLine("开始执行子线程.... "); Thread.Sleep(100); }
执行结果:
上面的代码,大家应该都很好理解,通过new Thread 来创建一个子线程,然后.Start() 开始执行。
我们F12 ThreadStart 看到 public delegate void ThreadStart(); 是一个无参数无返回值的委托,那么,如果要在线程中执行一个有参数的方法怎么办了?
OK,我们看Thread的构造函数
ParameterizedThreadStart 是什么?按字面上意思就是带参数的ThreadStart,继续F12看看它
果然是可以带一个object的参数。
改造一下刚才的代码:
private static void Main(string[] args) { System.Console.WriteLine("主线程开始"); var thread = new Thread(new ParameterizedThreadStart(ThreadTest)); thread.Start(10); System.Console.WriteLine("主线程结束"); System.Console.ReadLine(); } private static void ThreadTest(object p) { System.Console.WriteLine("开始执行子线程.... 参数:{0} ", p); Thread.Sleep(100); }
执行结果:
(当然还可以用ThreadStart(()=>{ }) 直接用lambda表达式的方式,这里就不写示例代码了 )
看到上面的执行结果,子线程因为Thread.Sleep(100) ,所以每次都最后才打印出输出结果,那么你可能会疑问,如果我想等子线程执行完,我再执行主线程后面的代码,怎么办?
private static void Main(string[] args) { System.Console.WriteLine("主线程开始"); var thread = new Thread(new ParameterizedThreadStart(ThreadTest)); thread.Start(10); thread.Join(); System.Console.WriteLine("主线程结束"); System.Console.ReadLine(); } private static void ThreadTest(object p) { System.Console.WriteLine("开始执行子线程.... 参数:{0} ", p); Thread.Sleep(100); }
注意看, 加了这句 thread.Join() ,管他什么意思,我们先看看执行结果吧!
OK,是不是明白Join()的意义了?
2.ThreadPool
为什么有了Thread还要出现ThreadPool了?如果你的代码设计了大量使用Thread,那么有可能会超过系统最大的线程数导致崩溃,而且每次创建和销毁线程也是很耗资源,ThreadPool就可以帮你提高代码效率并管理你的线程。
这不是重点,今天重点是学习它的基础使用方法。
private static void Main(string[] args) { System.Console.WriteLine("主线程开始"); ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadTest), 1); System.Console.WriteLine("主线程结束"); System.Console.ReadLine(); } private static void ThreadTest(object p) { System.Console.WriteLine("开始执行子线程.... 参数:{0} ", p); Thread.Sleep(100); }
先看看WaitCallback的定义
一个带参数的委托,这就要求它的委托方法必须带一个object的参数了。
ThreadPool静态类通过QueueUserWorkItem()方法将工作函数排入线程池,它不需要我们主动的.Start(),那么他能不能Join()了?
我们点一下就知道了,它既不要你手动Start也没有Join这样的方法。
好了,简单学习Thread和ThreadPool后,发现他们构造函数中都是没有返回值的委托,如果我们要在主线程中获取子线程执行方法的返回值,怎么办? Task闪亮登场了!
3.Task
private static void Main(string[] args) { System.Console.WriteLine("主线程开始"); // new Task 创建方式-不带参数 //Task task = new Task(ThreadTest); //task.Start(); // new Task 创建方式-带参数 //Task task=new Task(() => ThreadTest(10)); //Task.Factory 创建方式-不带参数 //Task task = Task.Factory.StartNew(ThreadTest); //task.Start(); //Task.Factory 创建方式-带参数 //Task task = Task.Factory.StartNew(() => ThreadTest(10)); //task.Start(); Task task = Task.Run(() => ThreadTest()); //Task task = Task.Run(() => ThreadTest(10)); System.Console.WriteLine("主线程结束"); System.Console.ReadLine(); }
Task 的三种创建线程的方法,Task.Run() 不需要手动Start() 。其他两种方式是需要手动Start()。 他们没有Join()方法,取而代之的是Wait()
我们用Run()方法为例,看Task如何获取方法的返回值。
private static void Main(string[] args) { System.Console.WriteLine("主线程开始"); Task<int> task = Task.Run(() => ThreadTest(10)); var result = task.Result; System.Console.WriteLine("主线程结束,result={0}", result); System.Console.ReadLine(); } private static int ThreadTest(int i) { Thread.Sleep(100); System.Console.WriteLine("子线程开始"); return i * 100; }
执行结果:
通过task.Result 我们获取到了在子线程中ThreadTest方法的返回值。有没有注意,主线程是等子线程执行完之后才打印最后输出的! task.Result 除了拿到返回值外,是不是和Wait()类似?
看到这里,你肯定会想到,这样另起线程去跑耗时作业和我们平时普通写法有什么区别?效率上会高很多吗?我们来测试看看!
常规方法:
private static void Main(string[] args) { DateTime dt1 = DateTime.Now; int count = 0; for (int i = 0; i < 10; i++) { Thread.Sleep(10); count += i; } System.Console.WriteLine("执行完成,耗时=" + (DateTime.Now - dt1).TotalMilliseconds); System.Console.ReadLine(); }
Task方法:
private static void Main(string[] args) { DateTime dt1 = DateTime.Now; Task<int> task = Task.Run(() => { int count = 0; for (int i = 0; i < 10; i++) { Thread.Sleep(10); count += i; } return count; }); var result = task.Result; System.Console.WriteLine("执行完成,耗时=" + (DateTime.Now - dt1).TotalMilliseconds); System.Console.ReadLine(); }
这就很尴尬了,用Task反而执行时间更长!!! 是不是我的打开方式不对?
说了大半天,估计你最期待的async await 还没见身影! 先放下上面的疑问,喝口水,我们来看看今天的主角async await!
4.async await
async是修饰一个异步方法的关键字。有两种返回类型(void 或者 Task<T>)await必须是在async修饰的异步方法体内,await后面必须是一个异步方法或者Task。表示异步等待后面方法的结果。
1.返回void的使用方法
private static void Main(string[] args) { System.Console.WriteLine("主线程开始"); for (int i = 0; i < 10; i++) { ThreadTest(i); } System.Console.WriteLine("主线程执行完成"); System.Console.ReadLine(); } private static async void ThreadTest(int i) { await Task.Run(() => { Thread.Sleep(10); System.Console.WriteLine("子线程开始,i=" + i); }); }
执行结果
2.返回Task<T>的使用方法
private static void Main(string[] args) { System.Console.WriteLine("主线程开始"); var result = ThreadTest().Result; System.Console.WriteLine("主线程执行完成,result="+ result); System.Console.ReadLine(); } private static async Task<int> ThreadTest() { var count = 0; await Task.Run(() => { for (int i = 0; i < 10; i++) { Thread.Sleep(10); count += i; System.Console.WriteLine("count="+ count); } }); return count; }
返回的是Task<T>,那么像得到它的返回值,肯定也是通过.Result了,我们肯定有疑问了,这样和直接写Task有什么区别? 只是为了更加方便和美观吗?
接下来我们来测试下执行效率!
private static void Main(string[] args) { DateTime dt1 = DateTime.Now; var t = ThreadTest(); System.Console.WriteLine("执行完成,耗时=" + (DateTime.Now - dt1).TotalMilliseconds + " count=" + t.Result); System.Console.ReadLine(); } private static async Task<int> ThreadTest() { var count = 0; await Task.Run(() => { for (int i = 0; i < 10; i++) { Thread.Sleep(10); count += i; } }); return count; }
我的天,这效率 !!!!
但有个问题一直没有想通, 如果这样写 (var result = ThreadTest().Result;) 那么耗时和之前的差不多, why? 召唤大神!!!! 跪求解释!
相关文章推荐
- Thread,ThreadPool,Task, 到async await 的基本使用方法和理解
- 使用Task代替ThreadPool和Thread
- 谈谈Android其中SoundPool基本使用方法
- 改善C#程序的建议9:使用Task代替ThreadPool和Thread
- 通过线程池使用多线程并发:ThreadPoolTaskExecutor 的应用例子
- WPF 使用Task代替ThreadPool和Thread
- 使用Task代替ThreadPool和Thread
- 使用 Task 替换 ThreadPool ,异步监测所有线程(任务)是否全部执行完毕
- Spring的线程池ThreadPoolTaskExecutor使用案例
- 使用Spring中的线程池ThreadPoolTaskExecutor实现JAVA并发
- .NET 4.5 使用async和await关键字调用异步方法
- 改善C#程序的建议9:使用Task代替ThreadPool和Thread
- 改善C#程序的建议9:使用Task代替ThreadPool和Thread
- spring中的ThreadPoolTaskSchedule使用
- 改善C#程序的建议9:使用Task代替ThreadPool和Thread
- 使用Task代替ThreadPool和Thread
- 改善C#程序的建议9:使用Task代替ThreadPool和Thread
- MariaDB中的thread pool详细介绍和使用方法
- BeginInvoke、ThreadPool、Task三类异步方法的区别和速度比较
- .NET(C#):await返回Task的async方法