最近学习到了爬虫进阶部分。如果下载数据量非常大时,采取之前的同步或串行编程方式,效率非常低,可能十天半个月都下不完。所以就必须要引出异步爬虫。
异步爬虫实现的方式可以分为:多线程、多进程、进程池、线程池、协程的方式。这两天的时间,主要研究这几种编程方式,先从基本思想入手,逐步实现爬虫的异步编程。
先来说什么是进程和线程:
进程(process)和线程(thread)的概念来自《操作系统》。
进程(Process)是系统进行资源分配和调度的基本单位。进程在业界没有明确的定义。
人们经常混淆或搞不清的是进程和程序之间的关系、进程和线程之间的关系。
程序和进程有何关系呢?
程序是死的,而进程是活的。程序要被计算机执行,要先转化为进程才可以。程序是死的,是写在硬盘或磁盘上的一串二进制代码,而进程是在内存中运行的“动态代码”。
一个程序至少对应一个进程,或者说程序和进程之间的对应关系是一对多。
这是win系统常见的任务管理器,我只开了一个UC浏览器,但是在计算机内部有多个UC浏览器的进程。
进程和线程有何关系呢?
前边提到了,进程是计算机分配资源的基本单位,线程是CPU调度和分派的基本单位。一个进程至少有一个线程,或者说进程和线程也是一对多的关系。应该说计算机先产生进程,再由进程产生线程。
所以线程使用的资源是所属进程的资源。在一个进程内部,所有的线程是共享该进程的大部分资源,而每个线程自己又有一丢丢的个有资源。可以画一个示意图,一个进程包含2个线程:
独有的资源主要是描述进程或线程的信息。
从宏观来看,如果能做到进程的并发执行,确实能提高CPU效率,但是从微观来看,在同一时间,一个CPU内部,只能有一个进程运行,所以仍然是串行的,多个进程在时间片内来回切换。但是我们现在的计算机是多核CPU,比如我现在用的电脑,已经是16年的电脑了,是四核的:
所以,在程序设计时,可以利用多进程技术,提高程序计算效率,充分发挥计算机性能。但是需要注意,计算机还需要运行操作系统,进行日常运转,所以不可能把所有核的CPU都拿来计算。
多进程技术虽然好,但是有一个问题,进程之间的来回切换,开销相比线程的切换开销要大。因为进程和进程之间的资源不共享,而同一进程之间的线程资源共享。进程切换程序正确运行所需的状态组成的,这个状态包括存放在存储器中的程序的代码和数据,他的栈,通用目的寄存器的内容,程序计数器,环境变量以及打开文件描述符的集合。线程切换要少,最主要的一个区别在于进程切换涉及虚拟地址空间的切换而线程不会,因为每个进程都有自己的虚拟地址空间,而线程是共享所在进程的虚拟地址空间的,因此同一个进程中的线程进行线程切换时不涉及虚拟地址空间的转换。这方面知识在《操作系统》中会学,有点太专业了。简单点说,在计算机内部,线程切换要比进程切换快、简单、耗时少。为什么?因为同一线程之间资源共享,进程之间资源不共享。所以后边就引出了线程技术。
有了线程技术,我们就可以在一个进程中创建多个线程,让它们在“同一时刻”分别去做不同的工作了。这些线程共享同一块内存,线程之间可以共享对象、资源,如果有冲突或需要协同,还可以随时沟通以解决冲突或保持同步。不过,多线程技术不是万金油,它有一个致命的缺点:在一个进程内,不管你创建了多少线程,它们总是被限定在一颗CPU内,或者多核CPU的一个核内。这意味着,多线程在宏观上是并行的,在微观上则是分时切换串行的,多线程编程无法充分发挥多核计算资源的优势。这也是使用多线程做任务并行处理时,线程数量超过一定数值后,线程越多速度反倒越慢的原因。多进程技术正好弥补了多线程编程的不足,我们可以在每一颗CPU上,或者多核CPU的每一个核上启动一个进程,如果有必要,还可以在每个进程内再创建适量的线程,最大限度地使用计算资源解决问题。因为不在同一块内存区域内,和线程相比,进程间的资源共享、通信、同步等,都要麻烦得多,受到的限制也更多。
所以通过上述分析,多进程、多线程没有说谁最好,谁不好,只能说各有所长。