个人猜想
为什么 CPU 主频很难超过 4GHz? —— 知乎
需要权衡的瓶颈包括是内存读取瓶颈,指令行并行处理瓶颈,和散热瓶颈。而散热瓶颈被认为是最难以跨越的。
惊人的服务器核数:
既然cpu的主频因为散热的限制无法增快,那么多核的替代方式成为必然。那么多线程,多进程编程也至关重要。
进程的基本概念
进程图
进程图是用于描述一个进程的家族关系的有向树,如图2-11所示。图中的结点(圆圈)代表进程。在进程D创建了进程I之后,称D是I的父进程(ParentProcess),I是D的子进程(ProgenyProcess)。
了解进程间的这种关系是十分重要的。因为子进程可以继承父进程所拥有的资源,例如,继承父进程打开的文件,继承父进程所分配到的缓冲区等。当子进程被撤消时,应将其从父进程那里获得的资源归还给父进程。此外,在撤消父进程时,也必须同时撤消其所有的子进程。
初始化进程控制块PCB
线程的基本概念
线程的引入
如果说,在操作系统中引入进程的目的,是为了使多个程序能并发执行,以提高资源利用率和系统吞吐量,那么,在操作系统中再引入线程,则是为了减少程序在并发执行时所付出的时空开销,使OS具有更好的并发性。
在多线程OS中,通常是在一个进程中包括多个线程,每个线程都是作为利用CPU的基本单位,是花费最小开销的实体。
线程具有许多传统进程所具有的特征,所以又称为轻型进程(Light-WeightProcess)或进程元,相应地把传统进程称为重型进程(Heavy-WeightProcess),传统进程相当于只有一个线程的任务。在引入了线程的操作系统中,通常一个进程都拥有若干个线程,至少也有一个线程。
内核支持线程的实现
每当进程要创建一个线程时,便为新线程分配一个TCB,将有关信息填入该TCB中,并为之分配必要的资源,如为线程分配数百至数千个字节的栈空间和局部存储区,于是新创建的线程便有条件立即执行。当PTDA图2-15任务数据区空间中的所有TCB空间已用完,而进程又要创建新的线程时,只要其所创建的线程数目未超过系统的允许值(通常为数十至数百个),系统可再为之分配新的TCB空间;在撤消一个线程时,也应回收该线程的所有资源和TCB。
java中的进程和线程
java进程
Java编写程序都运行在在Java虚拟机(JVM)中,在JVM的内部,程序的多任务是通过线程来实现的。每用java命令启动一个java应用程序,就会启动一个JVM进程。在同一个JVM进程中,有且只有一个进程,就是它自己。在这个JVM环境中,所有程序代码的运行都是以线程来运行。一个JVM进程对应一个JAVA程序。一个java的应用程序对应于一个JVM实例,当一个java程序运行的时候,一个jvm实例就诞生了。
java线程
在JDK1.2中,线程模型替换为基于操作系统原生线程模型来实现。因此,在目前的JDK版本中,操作系统支持怎样的线程模型,在很大程度上决定了Java虚拟机的线程是怎样映射的,这点在不同的平台上没有办法达成一致,虚拟机规范中也并未限定Java线程需要使用哪种线程模型来实现。线程模型只对线程的并发规模和操作成本产生影响,对Java程序的编码和运行过程来说,这些差异都是透明的。
对于SunJDK来说,它的Windows版与Linux版都是使用一对一的线程模型实现的,一条Java线程就映射到一条轻量级进程(LWP)之中,因为Windows和Linux系统提供的线程模型就是一对一的。
一对一线程模型
LWP :轻量级进程(Light Weight Process,LWP)
KLT :内核线程(Kernel-LevelThread,KLT)就是直接由操作系统内核(Kernel,下称内核)支持的线程,这种线程由内核来完成线程切换,内核通过操纵调度器(Scheduler)对线程进行调度,并负责将线程的任务映射到各个处理器上。
python中的进程和线程
python线程
创建线程
|
|
但如果为线程实例添加t.setDaemon(True)之后,如果不加join语句,那么当主线程结束之后,会杀死子线程。
线程锁
当访问某个资源之前,用Lock.acquire()锁住资源,访问之后,用Lock.release()释放资源。
|
|
线程池
|
|
Python多线程的缺陷
上面说了那么多关于多线程的用法,但Python多线程并不能真正能发挥作用,因为在Python中,有一个GIL,即全局解释锁,该锁的存在保证在同一个时间只能有一个线程执行任务,也就是多线程并不是真正的并发,只是交替得执行。假如有10个线程炮在10核CPU上,当前工作的也只能是一个CPU上的线程。
Python开始支持多线程。而解决多线程之间数据完整性和状态同步的最简单方法自然就是加锁。 于是有了GIL这把超级大锁,而当越来越多的代码库开发者接受了这种设定后,他们开始大量依赖这种特性(即默认python内部对象是thread-safe的,无需在实现时考虑额外的内存锁和同步操作)。但当大家试图去拆分和去除GIL的时候,发现大量库代码开发者已经重度依赖GIL而非常难以去除了。
为了直观的理解GIL对于多线程带来的性能影响,这里直接借用的一张测试结果图(见下图)。图中表示的是两个线程在双核CPU上得执行情况。两个线程均为CPU密集型运算线程。绿色部分表示该线程在运行,且在执行有用的计算,红色部分为线程被调度唤醒,但是无法获取GIL导致无法进行有效运算等待的时间。 GIL Performance 由图可见,GIL的存在导致多线程无法很好的立即多核CPU的并发处理能力。
由于Python的GIL的限制,多线程更适合于I/O密集型应用(I/O释放了GIL,可以允许更多的并发),而不是计算密集型应用。对于后一种情况而言,为了实现更好的并行性,你需要使用多进程,以便让CPU的其他内核来执行。
python进程
创建进程
|
|
使用进程池
|
|