在学习多线程以前,先来了解下几个与多线程相关的概念。web
进程:进程是计算机的概念,程序在服务器运行时占据所有计算资源的总和,一个应用程序运行起来就是一个进程,打开windows的任务管理器,以下图数据库
线程:线程也是计算机的概念,线程是进程的最小单位,也是程序在响应操做系统时的最小单位,一个进程至少由一个线程(主线程)构成。线程和进程同样也会占据必定的CPU、内存、网络、硬盘IO等。一个线程隶属于某个进程,进程销毁,线程也随之销毁。windows
句柄:是一个long类型的数字,是操做系统用来标识应用程序的,有点主键或者身份证号码的意思。服务器
多线程:一个进程或者说一个应用程序有多个线程在运行参与计算。网络
Thread类是C#语言对线程对象的封装。在.netframework1.0开始出现。在后面的多线程系列文章中会讲到在不一样的.netframework版本中多线程的API使用,在本篇文章中,先来初步认识多线程。多线程
1:CPU的多核技术和模拟核技术:并发
如计算机的参数概念4核8线程,所谓的4核8线程,4核指的是物理核心。经过超线程技术,用一个物理核模拟两个虚拟核,每一个核两个线程,总数为8线程。四核八线程采用的超线程技术,是指每一个CPU核心没有满负荷运载时,其剩余用量能够模拟成虚拟的核心。单个物理核同一时间点只能处理一个线程,经过超线程技术能够实现单个物理核实现线程级别的并行计算。异步
2:CPU分片:实际上CPU在同一时刻只能处理一个任务,可是由于CPU的计算能力强大,在1秒内能够响应不一样的任务,把1秒的处理能力分红10份,1到100毫秒处理任务A,101到200毫秒处理任务B,201到300毫秒处理任务C…,从宏观角度来看,感受多个任务在并发执行,这个就是CPU的分片。性能
同步方法:发起调用,完成后才继续下一行;很是符合开发思惟,有序由上至下执行;学习
异步方法:发起调用,不用等待完成,直接进入下一行,启用一个新的线程来完成计算。
同步方法就像真诚的请人吃饭,客人说他有事,须要忙一下子,请吃饭的人等待客人忙完了再一块儿吃饭。异步方法就像客气的请人吃饭,客人说他有事,请吃饭的人说那你先去忙吧,而后本身就去吃饭了。
同步方法卡界面,主线程(UI)线程忙于计算,无暇他顾,异步方法不卡界面:主线程闲置,计算任务交给子线程完成,改善用户体验。如在winform中点击按钮采用同步的方式调用一个复杂的任务计算会致使界面短暂卡死,直到任务计算结束才能够操做界面。
在web应用中发个短信通知,记录一个日志,均可以采用异步的方式去执行,客户端不用等到短信发送成功或者日志记录成功才能接受到服务端的响应。
为了可以清楚的说明状况,这里采用测试程序对比的方式,测试程序界面以下:
计算任务:
private void DoSomeThing(string btnName) { Console.WriteLine($"{btnName} 开始,当前线程id:{Thread.CurrentThread.ManagedThreadId}"); long lResult = 0; for (long i = 0; i < 1_000_000_000; i++) { lResult += i; } Console.WriteLine($"{btnName} 结束,当前线程id:{Thread.CurrentThread.ManagedThreadId}"); }
同步方式调用:
private void BtnSync_Click(object sender, EventArgs e) { Console.WriteLine($"btnSync_Click Start {Thread.CurrentThread.ManagedThreadId.ToString("00")}" + $" {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}"); Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); for (int i = 0; i < 5; i++) { string name = string.Format($"btnSync_Click_{i}"); this.DoSomeThing(name); } stopwatch.Stop(); Console.WriteLine($"同步方法耗时:{stopwatch.ElapsedMilliseconds}"); Console.WriteLine($"DoSomeThing End {Thread.CurrentThread.ManagedThreadId.ToString("00")} " + $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}"); }
同步方式调用执行结果:
同步方式调用时CPU的使用状况:
异步方式调用:
private void BtnAsync_Click(object sender, EventArgs e) { Console.WriteLine($"btnAsync_Click Start {Thread.CurrentThread.ManagedThreadId.ToString("00")}" + $" {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}"); Stopwatch stopwatch = new Stopwatch(); List<Task> tasks = new List<Task>(); stopwatch.Start(); for (int i = 0; i < 5; i++) { int k = i; tasks.Add(Task.Run(()=> { string name = string.Format($"btnAsync_Click{k}"); this.DoSomeThing(name); })); } Task.Run(()=> { Task.WaitAll(tasks.ToArray()); stopwatch.Stop(); Console.WriteLine($"异步方法耗时:{stopwatch.ElapsedMilliseconds}"); Console.WriteLine($"DoSomeThing End {Thread.CurrentThread.ManagedThreadId.ToString("00")}" + $" {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}"); }); }
异步方式调用执行结果:
异步方式调用时CPU的使用状况:
同步方法慢,上图耗时(16402毫秒),由于只有一个线程计算。异步方法快,上图耗时(10524毫秒),由于有多个线程参与计算。观察同步和异步调用时的使用状况折线图分析得知:多线程其实就是资源换取性能。在一个应用程序中是否是开启的线程越多越好?不是的,由于开启更多的资源,须要消耗更多的计算机资源,资源是有限的、资源调度也须要消耗资源,就像项目经理管理开发人员保证项目进度,项目经理调度(管理)开发人员也是须要工资的。
一个订单表统计很耗时间,能用多线程优化性能么?不能!这操做只包含一个任务,没办法并行计算,就像一个老师不能同时在两个班级讲课。若是一个操做在查询数据库的同时,须要调用接口、读写硬盘文件、作数据计算,这个能够用多线程优化性能,由于多个任务能够并行计算。
同步方法有序进行,异步多线程无序
启动无序:线程资源是属于非托管资源,是程序向操做系统申请的,由操做系统的调度策略决定,因此启动顺序是随机的,cpu使用同一个线程计算同一个任务,执行时间也是不肯定的,so,结束也是无序的。在使用多线程的时候必定要当心,尤为是多线程间有顺序要求的时候经过延迟一点时间(Thread.Sleep())来控制执行顺序,这是不靠谱的。