一、进程与线程区别
- 根本区别
进程是操作系统资源分配的基本单位。每个进程都有自己独立的地址空间、数据、堆栈和状态
线程是处理器任务调度和执行的基本单位。一个进程可以有多个线程,这些线程共享进程的地址空间和资源 - 资源开销(内存分配)
进程创建新的进程需要分配独立的地址空间,有较大的字节。进程间切换和通信成本相对较高
线程间共享地址空间和资源,因此线程的创建、切换和通信相对较接近,资源消耗较少 - 通信方式
进程间通信相对复杂,主要方式有管道、消息队列、信号量、共享内存等
线程间通信更简单,可以直接访问其他线程的数据(因为它们共享地址空间),主要通过锁、信号量等同步原语来协调(也可以共享变量,共享内存,共享数据库,消息队列) - 隔离性与安全性:
进程间有最大的隔离性,一个进程崩溃(例如访问了内存)一般不会影响其他进程。
线程间共享进程资源,一个线程的错误可能影响同一进程的其他线程。 - 使用场景
IO密集型使用线程,计算密集型使用多进程
进程适用于相互独立,需要较高安全性和隔离性的场景。
线程适用于需要密集通信,或共享大量资源的场景,如服务器的请求处理 - 补充
协程:是一种用户态的轻量级线程,协程的调度完全由用户控制。
协程拥有自己的寄存器上下文和栈,协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。
二、什么是GIL锁?GIL对Python性能的影响
-
GIL全局解释锁
全局解释锁
: 每个线程在执行过程的过程都需要先获取GIL,确保在同一时刻只有一个线程可以执行字节码,目的
是简化CPython的设计, 保证线程安全 -
线程释放GIL锁的情況:
- 在I0操作等可能会引起阻塞的system call之前,可以暂时释放GIL,但在执行完毕后,必须重新获取GIL
- Python 3.x使用计时器(执行时间达到國值后,当前线程释放GIL)或Python 2.x, tickets计t数达到100
python使用多进程是可以利用多核的CPU资源的。
多线程爬取比单线程性能有提升,因为遇到I0阻塞会自动释放GIL锁
多进程适合在 CPU 密集型操作(Cpu 操作指令比较多,如位数多的浮点运算)。
多线程适合在 IO 密集型操作(读写数据操作较多的,比如爬虫) -
GIL全局解释锁:
目的
是简化CPython的设计, 保证线程安全- 保护内存管理:
GIL保证了对Python对象的访问是线程安全的。Python使用引用计数来管理内存,而更新引用计数并不是一个原子操作。假如两个线程同时修改同一个对象的引用计数,可能会导致引用计数被错误地更新,进而引发内存泄漏或错误地回收了仍在使用的对象。GIL确保了每一时刻只有一个线程在执行Python字节码,从而避免了这种竞争条件 - 简化设计和实现:
通过使用GIL,CPython解释器的实现大大简化。GIL消除了多线程环境中复杂的锁管理和同步问题。没有GIL,CPython的每个数据结构都需要单独管理锁,这将增加设计和实现的复杂性
- 保护内存管理:
三、为啥python的多线程是假的,还用多线程
为什么说Python的多线程是“假”的:
GIL是一个全局锁,它保证一次只有一个线程执行Python字节码。在多核CPU环境中,这意味着Python的多线程不能有效利用多个CPU核心。相反,尽管有多个线程,但在任何给定的时刻,只有一个线程在执行。这使得Python的多线程对于计算密集型任务效果不佳,因为它们无法真正地执行任务。
为什么仍然使用Python的多线程
对于I/O密集型任务(如网络通信、文件I/O等),多线程仍然是非常有效的。
在I/O密集型任务中,线程大部分时间都在等待外部资源
比如等待文件读取写完成或网络响应。在此期间,CPU几乎处于空闲状态。
通过使用多线程,当一个线程等待I/O操作完成时,GIL可以被释放,其他线程可以继续执行。
这样,多线程可以在I /O密集型应用中提高程序的总体性能和响应能力- 综上所述
尽管Python的多线程因GIL的存在而受到了一定的限制(特别是对于计算密集型任务),它仍然在某些场景下是有用和有效的。
选择是否使用多线程,以及如何使用,取决于具体的应用场景和性能需求。
四、为啥 IO密集型使用线程,计算密集型使用多进程
3.1 I/O密集型任务:
I/O密集型任务是指系统的性能瓶颈主要出现在I/O操作上,如文件操作、网络通信等
为什么使用线程??
- I/O 密集型任务中,CPU 的使用并不是瓶颈,主要时间消耗在等待 I/O 操作上。在此期间,CPU 大部分时间都是空闲的。
- 线程的创建和思考成本很小,且所有线程共享进程的内存空间,使得线程之间的通信和数据交换成本很低。
- 使用线程可以使一个进程内的多个线程在等待I/O操作时可以共享同一个资源,这样当一个线程在等待I/O时,其他线程可以继续执行
3.2 计算密集型任务:
计算密集型任务是指系统的性能瓶颈主要出现在CPU计算上,即任务需要大量的CPU计算时间
为什么使用多进程??
- 对于计算密集型任务,多进程可以有效地利用多核CPU的优势,因为每个进程可以在一个单独的CPU核上执行。
进程间的独立性较强,一个进程的崩溃不会影响到其他进程。
在解释全局器锁(GIL)的存在的环境下(例如CPython),多线程程序无法有效利用多核CPU。GIL是Python的一个互斥锁,它阻止多个线程同时执行Python字节码。
密集型任务,这可能导致程序的效率大幅度降低。多进程则参与GIL的限制,每个进程都有自己的GIL和独立的内存空间,因此可以充分利用多核CPU。
五、进程间8种通信方式详解
- 管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常
是指父子进程关系 - 消息队列通信:消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载
无格式字节流以及缓冲区大小受限等缺点 - 信号量:信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源
时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。 - 信号:信号是种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
- 共享内存通信:共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内
存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实来实现进程间的同步和通信。 - socket:套接口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同机器间的进程通信
为什么爬虫使用多线程?而不是多进程
- 多进程占用资源多,进程间通讯不方便,爬虫很少用
- 多线程/协程 在请求数据和解析数据的时候采用多线程,大部分时间都是单线程爬虫,大部分情况采用分布式
六、什么是并发和并行?同步和异步,阻塞和非阻塞的区别?
# 并发:同一时刻只能处理一个任务,但可以交替处理多个任务。(一个处理器同时处理多个任务〕
# 并行:同一时刻可以处理多个任务。(多个处理器或者是多核的处理器同时处理多个不同的任务)
# 类比:并发是一个人同时吃三个馒头,而并行是三个人同时吃三个馒头。线程是并发,进程是并行;进程之间相互独立,是系统分配资源的最小单位,同一个线程中的所有线程共享资源。
阻塞:在执行一个操作时,不能做其他操作;
非阻塞:在执行一个操作时,能做其他操作。阻塞和非阻塞关心的问题是:能不能做其他操作。
IO多路复用方式: select、poll、epoll
select : (socket最大连接数1024)不断轮询每个socket对象
epoll :(链接数量没有限制的) 给每个socket对象绑定一个回调函数
路由分发器: 匹配正则调用相应处理器类
indexhandler: 具体的页面
并发:同一个时间,单CPU只执行一个任务
并行:多CPU同时执行不同任务
同步和异步针对结果而言:
同步: 对请求结果来说,上一步的操作必须执行完毕,下一步才能执行
异步: 下一步的操作不需要等待上一步的完成
阻塞和非阻塞针对线程的状态而言:
阻塞: 线程没有资源,因此挂起并不执行
非阻塞: 线程有资源,一直运行
Tornado是异步非阻塞的框架
阻 塞 、 非 阻 塞
阻 塞 是 指 调 用 函 数 时 候 当 前 线 程 被 挂 起 。
非 阻 塞 是 指 调 用 函 数 时 候 当 前 线 程 不 会 被 挂 起 , 而 是 立 即 返 回 。
同 步 、 异 步
同 步 是 你 告 诉 老 婆 做 饭 , 然 后 老 婆 就 开 始 做 饭 , 你 就 一 直 等 待 饭 上 桌 。
异 步 是 , 你 告 诉 考 婆 让 她 去 做 饭 , 如 果 老 婆 同 意 会 立 即 返 回 1, 你 等 待 老 婆
做 好 , 你 可 以 干 其 他 事 情 , 做 好 之 后 老婆 总 有 通 知 你 的 方 式 〔( 比 如 你 在 玩 电
脑 你 老 婆 揪 着 你 的 耳 朵 去 吃 饭 ) 。 如 果 老 婆 不 做 , 她 也 会 立 刻 通 知 你 , 返 回
-1, 这 时 候 也 不 会 阻 塞 。 当 然 还 有 一 种 是 你 老 婆 答 应 你 了 , 结 果 过 了 一 会 她
大 喊 一 声 , 劳 资 不 做 了 ! 但 也 会 通 知 你 。 但 是 最 后 吃 饭 到 你 需 要 自 己 去 先 获
取 到 老 婆 的 通 知 然 后 再 去 端 饭 。
同 步 和 异 步 关 注 的 是 获 取 结 果 的 方 式 。
同 步 是 获 取 到 结 果 之 后 才 进 行 下 一 步操 作 ,
阻塞 非 阻塞 关 注 的 是 接 口 时 当 前 线 程 的 状 态 ,
同 步 可 以 调 用 际 塞 也可 以 非 阻 塞 。 异 步 是 调 用 非 阻 塞 接 口