玩转python(1)多线程的历史1
2017年年底,我从老东家离职。离职后我和小伙伴创业,负责确定技术栈以及服务端的开发部署。在进行了综合考虑之后,我决定使用python作为我们的后端语言。虽然只有一年半的工作经验,还是个菜鸟,但是我也明白一个优秀的程序员不应该只满足功能的实现,更要对性能有不懈的追求,这里要提到的多线程就是一种提高性能的手段。
不过,python的多线程和其他的语言的多线程是有所不同的。我上一份工作是java开发,这几天查阅了一些python的资料之后,我认为python的多线程和java的多线程之间有巨大差别。相同的问题,java的解决方案未必可以套用在python上。因此,必须对python这门语言进行深入的学习。
无论用哪种语言进行多线程编程,首先要了解线程这个概念。线程的定义这里不再赘述,网上有很多,不过要记住,线程是操作系统层面上的概念。如果不使用操作系统,直接让程序在计算机上运行是怎样的呢?先说明一下,我没这么干过:P,因为这样做很麻烦,玩嵌入式或者需要经常和底层固件打交道的朋友应该深有体会。大致的流程是这样:
- 程序的二进制机器码被载入内存,CPU各个寄存器被初始化,指令指针寄存器指向第一条指令(这个寄存器保存指令在内存中的地址);
- CPU根据指令指针寄存器保存的地址通过电路从内存中取出指令,然后执行(取出指令后,指令指针寄存器会进行自增,取到的指令还要进行译码,这里涉及指令长度,指令流水等知识,不方便展开讲,感兴趣的可以自己查阅资料,推荐王爽的《汇编语言》);
- 重复步骤2直至程序结束。
在这个过程中,CPU内所有的寄存器都只为当前执行的这段程序提供服务。如果这段程序的执行被中途打断了,CPU去执行别的程序,现在要想这段可怜的程序继续执行,而不是重新执行,怎么办呢?其实只要在CPU执行别的程序之前,把寄存器里的值全部保存下来就行了。待CPU执行完别的程序,把事先保存的寄存器值恢复到原位就行了。
CPU的时钟频率现在越来越快,用python写一个累加1万次的程序,运行之后给我们的感觉是它可以瞬间给出答案。速度同样在变快的还有各种IO设备,不过相比于CPU的时钟频率,IO速度还是很慢的。所以,在进行IO操作时,CPU空闲,此时将CPU交给别的需要运算的程序会提高CPU的利用率。但是对那种大部分时间都在利用CPU的程序,还是不要使用这种操作的好,因为保存寄存器值,然后重新加载也很费时间(相对于CPU指令周期)。
其实上面介绍的就是多线程的一些底层细节,而把寄存器的值保存起来然后再重新加载的操作已经很接近于我们经常提到的上下文切换(具体实现略有不同)。上面也提到了CPU密集型和IO密集型程序,大家可以查阅比对一下CPU的指令周期,磁盘读写1字节所需时间,网卡读写1字节所需时间,这样会对使用多线程的重要性有一个直观的感受。