import time
import multiprocessing
gend=1
def count(n, t):
start = time.clock()
for i in range(n * t):
n += 0
end = time.clock()
print(end - start)
global gend
gend = time.clock()
print("Done")
n = 5000000
pool = multiprocessing.Pool(processes = 3)
gstart=time.clock()
for i in xrange(1, 4):
pool.apply_async(count, (n, i))
pool.close()
pool.join()
print("all run time: {0}".format(gend - gstart))
输出结果如下:
0.406385
Done
0.727774
Done
1.021318
Done
all run time: 0.928852
本文针对不同的语言分析它们对并发的支持,读者需要有操作系统的一些概念,如用户级线程和内核级别线程、进程和线程的区别,这些我在预备中只是会做一些相关的说明,保证能顺利理解此文。
0、预备知识【可选】
一、CPU的抢占
先来看看操作系统的进程转换图:
先不用管图中线条的颜色。
首先明确几个问题
什么时候会发生CPU的抢占
当CPU的中的一个时间片到期之后就会让出CPU,这个时候CPU就会根据调度规则从就绪队列中取出一个任务运行。
举个例子,就好比下面的过程 皇帝可能自身有个翻牌的规则,比如根据高矮胖瘦或者技术,翻一个最优妃子的牌,当然不同的皇帝可能翻牌的规则可能不一样,可能新皇帝比较懒,认为谁来的早谁伺候就行啦。
也就是转换图中的蓝色线就表示发生抢占。
什么时候会发生CPU的调度
当CPU空闲的时候,CPU还是满载就hang住了(貌似是废话哟)。
对应转换图中绿色和红色就会发生CPU的调度。
所有的调度会是抢占式调度么
CPU的调度也有两种,抢占式(对应转换图中的绿色线)和非抢占式调度(对应转换图中的红色线)。
抢占式调度和非抢占式调度发生在什么时候
转换为就绪状态就会发生CPU的抢占,也就是对应图中绿色的线(创建一个新进程的时候也会发生抢占,可能那个时候CPU可能正在忙,并不会发生抢占)。 下面分析一下抢占式的状态转换:
非抢占式调度就比较好理解了:
二、什么是并发
上面说完了什么是CPU的抢占。其实CPU的抢占过程就是并发的过程,并发就是指的能支持多任务,但是不一定是同一个时刻执行的,图行如下:
这就像如果是单核CPU的电脑的话,我们可以一边写代码一边听歌甚至一般打开着浏览器,这些对于使用者看来是同时执行的,但是归根到底还是一个CPU,因为CPU的切换的够快,所以你看不出来。
还有个并行比较容易和并发混淆,并行和并发的区别主要在同时上, 并行指的是能同时运行多个任务,并发指的是能运行多个任务,不一定同时。
知乎上有个很好的回答:并发与并行的区别?
三、进程
进程可以理解为是程序的动态表示,程序代码放在磁盘上就是静态文件,程序运行起来就是操作系统中的一个进程。
进程是操作系统分配资源的最小单位,比如我们任意语言的一个helloword程序,总会有变量,所以这就要有内存资源,以及对程序分配的堆栈信息。
四、内核线程和用户线程
线程也会分为内核线程和用户线程。 内核线程是直接由内核进程启动的进程(没错,线程是进程,是不是要迷糊了咯)。内核线程实际上是将内核函数委托给独立的进程,它与内核中的其他进程”并行”执行。
对于用户线程来说,对于内核是不感知的,用户级别的线程是由使用者控制的。用户通过线程控制库来控制用户线程的创建,撤销以及同步等功能,可以看下图,理解一下:
系统线程也叫轻量级进程,对于内核来说,进程是调度的最小单元。 对于多核计算机来说,进程可以被调度到不同的核上,如果是单核就会有抢占的情况。
1、单线程的javascript
2、”假“多线程的python
先不要在意我为什么说python的多线程是“假”的。我们先来看看一个同步的程序:
输出为:
然后我们再来看看高大上的多线程的版本是什么样子的。
再来看看运行的结果
现在知道我为什么要说他是假的了吧,多线程竟然比单线程还要慢,可能有经验的程序员就会说了,你这个肯定是在单核的计算机上跑的,由于CPU切换任务导致的耗时。 其实这句话可以说的也对也不对,因为我是在四核的MAC上跑的,就是30核的机器也是类似的结果,但是python的多线程是真的确确实实跑在一个核上的(针对cpython解释器)。
这个时候就必须扯一扯python的全局解释器锁(GIL)了,它就是一个全局锁,为了保证一段时间内只有一个线程去执行(加锁)。python解释器为什么要这么做呢,主要原因有两个:
当然python也是能够通过多进程指定并行的去执行:
输出结果如下:
是不是以为就完了呢,还记得javascript中的事件循环么,因为对于强io的情况过于高效了,所以在python中也支持了。
3、天生就支持并发的Go语言
4、依赖jvm的java
参考