深入理解:线程,进程,协程和并行,并发

爬虫的并发控制:

多进程、多线程、协程 yield


从硬件:

双核四线程(超线程技术):
有两个CPU核心,每个核心有两个逻辑处理器,相当于有四个CPU核心


四核四线程:
有一个CPU核心,每个核心有一个逻辑处理器,相当于有四个CPU核心


从操作系统:

进程和线程,都是CPU任务的执行单位。


进程:早期的操作系统是面向进程的:
表示一个程序的执行活动(打开、执行、保存、关闭)


线程:现在的操作系统都是面向线程:
表示一个进程处理任务时最小调度单位(执行功能a、执行功能b)


一个程序至少开启一个进程,一个进程至少有一个线程。

每个进程都有独立的内存空间,不同进程之间不共享任何状态。
进程之间的通信需要经过操作系统调度控制,通讯效率低、切换开销大。


同一个进程里的多个线程,是共享内存空间,切换开销小,通讯效率高。
线程的工作机制是"抢占式",出现竞争的状态,竞争意味着数据不安全。
引入了"互斥锁":让多个线程安全有序的访问内存空间的机制。

Python的多线程:
类似于 GIL(全局解释器锁):保证一个时间片里只有一个线程在运行。
好处:直接杜绝了多个线程的竞争问题:
坏处:Python的多线程不是真正的多线程。

Python解释器在处理IO阻塞类型的方法时,会释放GIL
如果没有IO操作,该线程会每隔 sys.getcheckinterval() 次释放GIL,让其他线程尝试执行。


并行:
同一CPU时间片内,CPU可以同时处理多个程序。如果有多个程序,同步执行。

程序1:----------------
程序2:----------------
程序3:----------------
程序4:----------------

并发:
同一CPU时间片,只能处理一个程序。如果有多个程序,交替执行。

程序1: ----- ------
程序2: -----
程序3: ----
程序4: -----


多进程:可以充分利用多核CPU的资源,适用于密集CPU任务(大量的并行运算)
Python的多进程模块:multiprocessing

进程之间通信成本高切换开销大,不适用于需要大量数据通信和切换的任务(爬虫)

设计模式:生产者消费者(并行模式)

多线程:适用于密集I/O任务(磁盘IO,内存IO,网络IO),切换开销小通信成本低。
Python的多线程:Thread、thraeding、multiprocessing.dummy

多线程:同一个时间片只能执行一个线程,无法充分利用CPU多核资源(只能做到并发,不能做到并行)

协程:操作系统和CPU不认识协程,是由程序员通过代码逻辑控制。
特点是在单线程下执行多个任务,且不需要通过操作系统切换(没有切换开销,也不需要处理锁),执行效率高。

Python: gevent ,猴子补丁(Python代码在执行网络IO阻塞时,会自动切换协程)

协程:适用于密集网络I/O任务

多进程爬虫:不合适

多线程爬虫:缺点 - 通过操作系统调度,有线程切换开销(海量URLs场景会增加CPU负载);优点 - 使用场景广泛(网络读写并发/数据库读写并发/磁盘读写并发)
协程爬虫:缺点 - gevent配合monkey.patch_all() 只能提高网络并发效率,不能处理其他并发场景;优点 - 通过程序员代码逻辑控制,不受操作系统调度,没有切换开销,降低CPU负载(处理海量URLs优势明显)


执行方式:
同步:执行一个任务,必须等待上一个任务完成(没有并发的爬虫)
异步:执行一个任务,不必等待上一个任务完成(并发的爬虫)

程序状态:
阻塞:程序执行时,必须等待该任务完成,否则保持等待状态。
非阻塞:程序执行时,不必等待该任务完成,可以继续执行下一个任务。

异步+非阻塞(效率最高):
发送请求后,不必等待响应返回,可以继续处理其他请求发送;当处理功能挂起时,能够立刻切换其他到功能继续执行。



异步网络框架 Twisted Tornada

Scrapy :请求处理模块+响应解析模块+twisted
scrapy-redis:Scrapy + Redis(在同一个数据库里处理请求去重、请求分配、数据存储)

单机爬虫:
分布式爬虫:

CPU -> 寄存器 -> CPU缓存L1/L2/L3 -> 内存 -> 硬盘/固态硬盘 -> 网络

猜你喜欢

转载自www.cnblogs.com/snailon/p/8945840.html