程序员应懂的三个名词之线程、进程与处理器
处理器:即中央处理器(CPU,Central Processing Unit),它是一块超大规模的集成电路,是一台计算机的运算核心(Core)和控制核心( Control Unit)。它的功能主要是解释计算机指令以及处理计算机软件中的数据。
进程:进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。
线程:线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。
我们不妨来提出几个问题:
多核处理器和多处理器是一个意思吗,运行效率一样吗?
答案是否定的。多核处理器是指一个CPU有多个核心处理器,处理器之间通过CPU内部总线进行通讯。而多处理器是指简单的多个CPU工作在同一个系统上,多个CPU之间的通讯是通过主板上的总线进行的。因此,多核CPU要比多个CPU在一起的工作效率更高(单核性能一致的情况下)。
单核CPU也能运行多个进程吗,能并发执行吗?
单核CPU也能执行多个进程,这点大家都知道。但是并不是真正意义上的同时执行(并发执行)。虽然在单核CPU的电脑也能开启多个进程,但这些进程并不能同时被开启和执行,而是以轮换的机制执行的(是有时间顺序的),而CPU处理某个单一的操作速度是极快的,并且极快的在进程中切换,从而让人感觉是同时运行了多个进程、同时处理多个操作。这么做的原因之一是防止前一个任务耗时太长,导致后面简单的任务等待太久,而且电脑中有许多系统进程必须同时处于开启状态,所以CPU也必须采取这种办法来处理。理论上讲,真正意义上的同时执行的进程数不能超过CPU核心数。
单核CPU也能运行多个线程吗,能并发执行吗?
同上理,单核CPU运行多个线程是可行的。因为线程是进程中的一个实体,一个进程可以开启多个线程,所以即使只开启单个进程也能开启多个线程在单核CPU上运行。但是同上理,单核CPU并不能真正意义上的实现线程并发。
现在我们知道了,生活或工作中时常提到的所谓线程并发并非真正意义上的并发执行,这取决于CPU。那么多线程实现并行处理还有必要吗?答案是肯定的。多线程可以实现并行处理,避免了某项任务长时间占用CPU时间。
对于单核单处理器(CPU)来说,为了运行所有这些线程,操作系统为每个独立线程安排一些CPU时间,操作系统以轮换方式向线程提供时间片,这就给人一种假象,好象这些线程都在同时运行。既然多线程实现并发确实能够提升性能,那是否使用多线程并发是必须的呢?答案是否定的,如果两个非常活跃的线程执行很简单的操作,为了抢夺对CPU的控制权,在线程切换时会消耗很多的CPU资源,反而会降低系统的性能。
最开始,线程只是用于分配单个处理器的处理时间的一种工具。但假如操作系统本身支持多个处理器,那么每个线程都可分配给一个不同的处理器,真正进入“并行运算”状态。从程序设计语言的角度看,多线程操作最有价值的特性之一就是程序员不必关心到底使用了多少个处理器,程序员只需将程序编写成多线程模式即可。程序在逻辑意义上被分割为数个线程;假如机器本身安装了多个处理器,那么程序会运行得更快,毋需作出任何特殊的调校。
使用多线程也会和多进程一样,会存在资源共享问题。如果有多个线程同时运行,而且它们试图访问相同的资源(共享的资源),这时就会出现问题。而一种可行的办法就是在使用期间必须进入锁定状态。所以一个线程可将资源锁定(例如,程序设计中的线程锁),在完成了它的任务后,再解开(释放)这个锁,使其他线程可以接着使用同样的资源。
多线程是为了同步完成多项任务,不是为了提高运行效率,而是为了提高资源使用效率来提高系统的效率。线程是在同一时间需要完成多项任务的时候实现的。
处理器结构对并发程序的影响:
对称多处理器是最主要的多核处理器架构。在这种架构中所有的CPU共享一条系统总线(BUS)来连接主存。而每一个核又有自己的一级缓存,相对于BUS对称分布,如下图:
这种架构在并发程序设计中,大致会引来两个问题,一个是内存可见性,一个是Cache一致性流量。内存可见性属于并发安全的问题,Cache一致性流量引起的是性能上的问题。
内存可见性:内存可见性在单处理器或单线程情况下是不会发生的。在一个单线程环境中,一个变量选写入值,然后在没有干涉的情况下读取这个变量,得到的值应该是修改过的值。但是在读和写不在同一个线程中的时候,情况却是不可以预料的。Core1和Core2可能会同时把主存中某个位置的值Load到自己的一级缓存中,而Core1修改了自己一级缓存中的值后,却不更新主存中的值,这样对于Core2来讲,永远看不到Core1对值的修改。在Java程序设计中,用锁,关键字volatile,CAS原子操作可以保证内存可见。
Cache一致性问题:指的是在SMP结构中,Core1和Core2同时下载了主存中的值到自己的一级缓存中,Core1修改了值后,会通过总线让Core2中的值失效,Core2发现自己存的值失效后,会再通过总线从主存中得到新值。总线的通信能力是固定的,通过总线使各CPU的一级缓存值数据同步的流量过大,那么总线就会成瓶颈。这种影响属于性能上的影响,减小同步竞争就能减少一致性流量。
另外,通常说的四核八线程,实际上是模拟八核。相当于在每个逻辑处理器上可以开两个线程。它的性能在一般情况下比四核四线程快不少,有时也能接近八核的性能。但如果真正遇到高并发(CPU使用率很高甚至100%)的时候,性能是没法和八核比的。因为四核八线程并不能真正意义上的实现八线程的并发(由上面的论述便可知)。