c# 并发编程系列之二:并发产生的问题
在上一篇中介绍了进程、线程、线程池的概念后,本篇我们再进一步,看看并发产生时操作系统是如何执行的,
以及并发给编程带来哪些和传统编程不一样的问题。
一、并发(Concurrency)
定义:同时做多件事情。
解释:比如GUI程序中用户输入数据时同时对数据做处理;WEB服务器同时处理多个用户请求等。这里同时指
的是一个时间区间,而不是一个时间点。
二、多线程(Multithreading)
从操作系统的角度来说,多线程是一个时间段内有多个线程在运行,这多个线程可以属于同一个进程也可以属于
不同的进程。
从编程的角度来说,多线程特指某个进程内有1个以上的线程在CPU上执行指令, 在这多个线程中,有一个是主线程,
其他的线程是由主线程创建的子线程,多线程是并发的微观表现形式。
三、多线程的2种运行情况
❒ 第一种:单核CPU
CPU的执行是分时间片来进行的。
在单核CPU的情况下,操作系统会分配一定的CPU时间片给线程去执行指令,线程在使用完时间片后被挂起,系统再将
CPU时间片分配给下一个线程,每个时间片依次轮流地执行各个应用程序的线程指令。由于一个时间片很短,相对于一个
应用程序来说,就好像是处理器在为自己单独服务一样,从而产生了多个应用程序在同时运行的效果。
操作系统是如何分配CPU时间片给线程的呢?
它是根据线程的优先级按一定的调度算法来对线程进行调度的。一般线程的优先级由两部分决定:自身在进程中的相对
优先级和所属进程的优先级。进程的调度算法有如下几种:
❐ 先来先服务 FCFS:First Come First Service
❐ 短作业优先/短进程优先 SJF / SPN:Shortest Job First / Shortest Process Next
❐ 高优先权优先 HPF:Highest Priority First
❐ 高响应比优先 HRRN:Highest Response Ratio Next
❐ 时间片轮转法 RR:Round-Robin
❐ 多级反馈队列 MFQ:Multilevel Feedback Queue
既然单核CPU在某个时间片内只能执行一个线程的指令,因此,要想我们的应用程序获得更快的执行速度,理论上应用程序
就应该开启多个线程来增加被CPU执行的次数。从而提高运行速度,这是我们进行多线程编程的出发点。
【补充】: 实际情况不一定如此,即使优先级相同,考虑到线程的创建、切换、相互间的通讯、同步、回收等都是有时间
开销的,所以在单核CPU的情况下,如果没有I/O等耗时操作,应用程序使用多线程并不一定运行更快,这个要特别注意。
单核CPU的结构如下图:
❒ 第二种:多核CPU
(1)多核CPU的产生
CPU的主频越高运行越快,但是主频的增长也是有瓶颈的,在这种情况下,要获得更快的计算速度和更低的功耗,
芯片设计者开始绕过主频,转而在一块CPU中增加多个核心(Core),以此来提高CPU的算力 。
(2)多核CPU的运行
在多核CPU的情况下,一个CPU有多个处理核心(即多个逻辑处理器,如下图,笔者的电脑就有12个),操作系统可以
将多个线程分配到不同的逻辑处理器去运行,让线程的执行做到真正的并行(Parallel)。
(3)多核CPU的调度
多核CPU的任务调度算法有全局队列调度和局部队列调度。
全局队列调度:操作系统维护一个全局的任务等待队列,当系统中有一个CPU核心空闲时,操作系统就从全局任务等待队列中
选取就绪任务开始在此核心上执行。这种算法的优点是CPU核心利用率较高。
局部队列调度:操作系统为每个CPU内核维护一个局部的任务等待队列,当系统中有一个CPU内核空闲时,便从该核心的任务
等待队列中选取恰当的任务执行,这种方法的优点是任务基本上无需在多个CPU核心间切换,有利于提高CPU核心局部Cache命中率。
目前多数多核CPU操作系统采用的是基于全局队列的任务调度算法。
(4) 任务的2种类型
❒ 计算密集型
要进行大量的计算,消耗CPU资源,比如数学计算、加密/解密等,因此任务数不宜过多;
❒ IO密集型
CPU消耗很少,大部分时间都在等待IO操作完成,比如网络连接、文件读写等,因此任务越多,CPU效率越高;
Inter(R) Core(TM) i5-4258 CPU示意图(双核四线程,3级缓存,L3多核心共享)
CPU线程和操作系统线程是不一样的概念:
❐ 操作系统调度的基本单位是(软件的)线程;系统的线程数有几十上百个;
❐ CPU运算的基本单位是(硬件的)线程;CPU中一个核心一般只有2个线程,数量固定;
❐ 操作系统负责把它产生的(软)Thread 调度到CPU中的(硬)Thread 中去,最后由CPU的(硬)Thread 来执行。
四、并发带来的问题
不管是单核CPU还是多核CPU,因为存在多个线程同时执行的情况,会产生下面的问题:
❐ 资源竞争
❐ 上下文切换
❐ 线程安全
❐ 同步
❐ 死锁
并发编程就是在利用多线程好处的时候处理好它带来的种种问题。