1.并发与并行的深入理解
计算机是如何执行程序指令的?
虽然现代生活中,计算机正在发挥着巨大的作用,为我们的生活带来便捷,为我们的工作带来高效。走近计算机,你会发现它似乎并不是那么无所不能,它只认识“0”和“1”。
回想我们使用计算机的过程,你会发现不外乎是这三个步骤:输入---处理---输出。你通过键盘等输入设备向计算机输入信息,计算机进行处理,然后在显示屏等输出设备上输出处理后的结果。
而操作系统则负责调度。
说到并发,不得不提一下cpu。因为,有不少人认为,单核cpu是无法并发的。其实,这是一种非常错误的观点。即使是单核cpu也可以一边听歌,一边写文档。(注意,我这里所说的并发强调的是实际效果,不是那些所谓的“理论上...”)
当然,还有的人根本不知道自己的cpu是不是单核。以win7为例,先在“计算机”上右键,点击属性。
点击设备管理器。
找到处理器并点击。
这时,处理器下有几个,便是几核。小叮当这里显示的是4核。
如果你非要说单核不能并发,我只能说那些单核时代一边听歌一边写文档的人怕是要从棺材里面站起来捶你几下。诚然,单核cpu因为只有一个逻辑处理核心,在理论上确实不能实现并发。
但是,单核cpu的时分多路复用让你感觉到等待了吗?不还是一边听歌一边写文档?在我看来,这就是并发!就像800岁的彭祖,他的寿命是有限吗?是,却也不是,相对于百年而过的普通人来说,他的寿命相对于你来说就是无限!
我觉得,并发,不应该简单的通过单核或是多核就来判定。而是要看实际的功效!而对于一个cpu来说,轮询调度则是很好的并发实现。所谓轮询,又称“程控输出入”(programmed i/o)。由cpu定时发出询问,依序询问每一个周边设备是否需要其服务,“有则服务”,服务结束后再询问下一个周边是否需要服务,就这样不断地周而复始。
轮询调度实现并发执行。就像酷狗不可能独占cpu一样,cpu也不会一直为酷狗服务。当运行的程序数量大于cpu的个数时,轮询便发生了,这由操作系统来调度,不需我们关心。
而现实生活中,我们在计算机上运行的程序数量往往超过了计算机拥有的cpu个数,所以,并发和并行应该是我们值得关心的问题。
那么什么是并发和并行呢?
并行:同一时刻内执行多个任务。也就是说,4个人分配4项工作,每人一项,同时刻执行。
并发:指两个任务都请求运行,而处理器只能按受一个任务,就把这两个任务安排轮流进行,由于时间间隔较短,使人感觉两个任务都在运行。就像多人访问服务器,服务器实际上是有服务队列的,但因为时间间隔很短,大家都认为,一请求服务器便响应。好像是单独为自己服务。这便是并发。
2.多进程实现并行
什么是进程?
简单的说,当一个程序运行了,这就是一个进程。比如,你想要听歌打开酷狗音乐,当酷狗音乐运行起来时,我们就可以说,酷狗音乐是运行在你计算机上的一个进程。
计算机程序是存储在磁盘上的可执行二进制(或其他类型)文件。只有把它们加载到内存中,并被操作系统调用,它们才会拥有其自己的生命周期。
进程则是表示的一个正在执行的程序。每个进程都拥有自己的地址空间、内存、数据栈以及其他用于跟踪执行的辅助数据。
操作系统负责其上所有进程的执行,并且操作系统会为这些进程合理地分配执行时间。
例如,我们可以使用多进程来减少任务的执行时间。
3.多线程实现并发
什么是线程?
线程被称作轻量级进程。多线程工作在同一个进程中,共享上下文。
可能有人会不太理解什么是上下文。所谓的上下文,大家可以将其理解为运行环境,或是资源。即多线程工作在同一个进程中,共享可用资源。
而线程执行也是轮询机制,区别于进程的操作系统调度,对于python来说,线程的轮询是由python解释器来调度的。
在python刚发展时期,市面上还没有多核处理器基本上都是单核的。而为了处理多线程间的一些安全问题,便出现了gil(global interpreter lock)——全局解释器锁。
通过gil我们可以实现在一个python进程里一次只执行一个线程。
值得注意的是,gil锁在遇到io操作时会自动切换线程。比如,当爬虫有多个请求时,我们可以通过切换线程在短时间内将请求全部发出,然后让这些请求一起等待返回结果,从而提高爬取效率。
对于计算密集型来说,纵使是多线程其也是一个进程,占用一个cpu。况且多线程间切换仍要耗时,所以对于计算密集型来说,多线程反而更加耗时。
如何在python中使用线程?
在python中,类似线程用法,使用threding.thread( )即可创建线程,使用start( )方法可开启线程。
为什么多线程不是并行?
python在设计的时候,还没有多核处理器的概念。因此,为了设计方便与线程安全,直接设计了gil锁。这个锁要求,任何进程中,一次只能有一个线程在执行。
因此,并不能为多个线程分配多个cpu。所以python中的线程只能实现并发,而不能实现真正的并行。
但是到了python3时,gil锁有了很棒的设计,在遇到阻塞(不是耗时)的时候,会自动切换线程。因此我们可以利用这种机制来有效的避开阻塞 ,充分利用cpu。
4.使用多进程与多线程来实现并发服务器
(1)多进程是并行执行, 相当于分别独立的处理各个请求。
(2)多线程,虽然不能并行运行,但是可以通过避开阻塞切换线程来实现并发的效果,并且不浪费cpu。
由于篇幅有限,在“小叮当python并发(三):走近进程与线程之“实战演练””中将为大家展现使用多进程与多线程来实现并发服务器的详细代码与实验结果。
5.多进程和多线程的选择
计算机常见的处理任务有计算密集型和io密集型。计算密集型需长期占用cpu,io密集型则不需长期占用cpu。
在python当中计算密集型用多进程, 多线程比单进程效率更低。而爬虫则属于io密集型,因为大多网络爬虫用的都是 http请求。
由于现实生活中运行的总进程数量总是多于 cpu核心数量!因此,严格来说并没有真正意义上的并行。现在运行的程序都是轮询调度产生的并行假象,所以如何根据实际情况选择多进程或是多线程已成为pyhon爬虫中的重中之重。
由于篇幅有限,在“小叮当python并发(三):走近进程与线程之“实战演练””中将为大家展现对多线程和多进程的详细代码与实验结果。