Python中多线程与多进程的恩恩怨怨
概念:
并发:当有多个线程在操作时,如果系统只有一个CPU,则它根本不可能真正同时进行一个以上的线程,它只能把CPU运行时间划分成若干个时间段,再将时间 段分配给各个线程执行,在一个时间段的线程代码运行时,其它线程处于挂起状。.这种方式我们称之为并发(Concurrent)。
并行:当系统有一个以上CPU时,则线程的操作有可能非并发。当一个CPU执行一个线程时,另一个CPU可以执行另一个线程,两个线程互不抢占CPU资源,可以同时进行,这种方式我们称之为并行(Parallel)。
最近在看Python的多线程,经常我们会听到老手说:“Python下多线程是鸡肋,推荐使用多进程!”,但是为什么这么说呢?
要知其然,更要知其所以然。所以有了下面的深入研究:
首先强调背景:
1. GIL是什么?
GIL的全称是Global Interpreter Lock(全局解释器锁),来源是python设计之初的考虑,为了数据安全所做的决定。
2. 每个CPU在同一时间只能执行一个线程
在单核CPU下的多线程其实都只是并发,不是并行,并发和并行从宏观上来讲都是同时处理多路请求的概念。但并发和并行又有区别,并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔内发生。
在Python多线程下,每个线程的执行方式:
获取GIL
执行代码直到sleep或者是python虚拟机将其挂起。
释放GIL
可见,某个线程想要执行,必须先拿到GIL,我们可以把GIL看作是“通行证”,并且在一个python进程中,GIL只有一个。拿不到通行证的线程,就不允许进入CPU执行。
在Python2.x里,GIL的释放逻辑是当前线程遇见IO操作或者ticks计数达到100(ticks可以看作是Python自身的一个计数器,专门作用于GIL,每次释放后归零,这个计数可以通过 sys.setcheckinterval 来调整),进行释放。
而每次释放GIL锁,线程进行锁竞争、切换线程,会消耗资源。并且由于GIL锁存在,python里一个进程永远只能同时执行一个线程(拿到GIL的线程才能执行),这就是为什么在多核CPU上,python的多线程效率并不高。
那么是不是python的多线程就完全没用了呢?
在这里我们进行分类讨论:
CPU密集型代码(各种循环处理、计数等等),在这种情况下,由于计算工作多,ticks计数很快就会达到阈值,然后触发GIL的释放与再竞争(多个线程来回切换当然是需要消耗资源的),所以python下的多线程对CPU密集型代码并不友好。
IO密集型代码(文件处理、网络爬虫等),多线程能够有效提升效率(单线程下有IO操作会进行IO等待,造成不必要的时间浪费,而开启多线程能在线程A等待时,自动切换到线程B,可以不浪费CPU的资源,从而能提升程序执行效率)。所以python的多线程对IO密集型代码比较友好。(下方有爬虫示例)
而在python3.x中,GIL不使用ticks计数,改为使用计时器(执行时间达到阈值后,当前线程释放GIL),这样对CPU密集型程序更加友好,但依然没有解决GIL导致的同一时间只能执行一个线程的问题,所以效率依然不尽如人意。
请注意:多核多线程比单核多线程更差,原因是单核下的多线程,每次释放GIL,唤醒的那个线程都能获取到GIL锁,所以能够无缝执行,但多核下,CPU0释放GIL后,其他CPU上的线程都会进行竞争,但GIL可能会马上又被CPU0拿到,导致其他几个CPU上被唤醒后的线程会醒着等待到切换时间后又进入待调度状态,这样会造成线程颠簸(thrashing),导致效率更低。
回到最开始的问题:经常我们会听到老手说:“python下想要充分利用多核CPU,就用多进程”,原因是什么呢?
原因是:每个进程有各自独立的GIL,互不干扰,这样就可以真正意义上的并行执行,所以在python中,多进程的执行效率优于多线程(仅仅针对多核CPU而言)。
所以在这里说结论:多核下,想做并行提升效率,比较通用的方法是使用多进程,能够有效提高执行效率
爬虫示例比较、下图是在双核CPU机器上执行单线程爬虫、多线程并发、多进程并行、所使用的时间与CPU使用率对比
单线程:

多线程并发执行:(线程池)

多进程并行执行:(进程池)

单线程VS多线程:在爬虫运行遇到IO阻塞时,多线程会切换到另外一条线程执行,某时刻该线程IO顺通并获得GIL锁后继续执行,CPU利用率高;单线程会等待阻塞,CPU利用率低;多线程比单线程快。
多线程VS多进程:多线程存在GIL锁,同一时刻只能有一条线程执行;在多进程中,每一个进程都有独立的GIL,不会发生GIL冲突;但在这个例子中,爬虫属于IO密集型,多进程适用于CPU计算密集型,所以用时较长,速度慢于多线程并发。
多线程、多进程VS单线程: 单线程惨败
Python中多线程与多进程的恩恩怨怨的更多相关文章
- python中多线程,多进程,队列笔记(一)
threading简介:If you want your application to make better use of the computational resources of multi- ...
- python中多线程,多进程,多协程概念及编程上的应用
1, 多线程 线程是进程的一个实体,是CPU进行调度的最小单位,他是比进程更小能独立运行的基本单位. 线程基本不拥有系统资源,只占用一点运行中的资源(如程序计数器,一组寄存器和栈),但是它可以与同属于 ...
- Python之多线程和多进程
一.多线程 1.顺序执行单个线程,注意要顺序执行的话,需要用join. #coding=utf-8 from threading import Thread import time def my_co ...
- 通过编写聊天程序来熟悉python中多线程及socket的用法
1.引言 Python中提供了丰富的开源库,方便开发者快速就搭建好自己所需要的应用程序.本文通过编写基于tcp/ip协议的通信程序来熟悉python中socket以及多线程的使用. 2.python中 ...
- Python 中多线程之 _thread
_thread模块是python 中多线程操作的一种模块方式,主要的原理是派生出多线程,然后给线程加锁,当线程结束的 时候取消锁,然后执行主程序 thread 模块和锁对象的说明 start_new_ ...
- 为什么在python中推荐使用多进程而不是多线程(转载)
最近在看Python的多线程,经常我们会听到老手说:"Python下多线程是鸡肋,推荐使用多进程!",但是为什么这么说呢? 要知其然,更要知其所以然.所以有了下面的深入研究: GI ...
- Python的多线程和多进程
(1)多线程的产生并不是因为发明了多核CPU甚至现在有多个CPU+多核的硬件,也不是因为多线程CPU运行效率比单线程高.单从CPU的运行效率上考虑,单任务进程及单线程效率是最高的,因为CPU没有任何进 ...
- Python【多线程与多进程】
import time,threading print("=======串行方式.并行两种方式调用run()函数=======")def run(): print('哈哈哈') # ...
- python中多线程
多线程 什么是多线程 开启线程的两种方式 进程和线程的区别 Thread对象的其他属性和方法 守护线程 死锁现象与递归锁 信号量.Event定时器 线程Queue 进程池和线程池 什么是多线程 在传统 ...
随机推荐
- PHP--Smarty的template模式
function change_year() { var ss = $('#select_year').children('option:selected').val(); $.ajax({ type ...
- Jboss配置HTTPS
配置jboss的HTTP请求走SSL(HTTPS协议) l 生成keystore 文件 用keytool生成server.keystore文件: 进入命令行 C:\Documents ...
- python基础--递归、三元表达式、列表(字典)生成式、匿名函数、常用的内置函数
函数的递归:函数在调用阶段直接或者间接的又调用自身 递归的两个阶段: 1.回溯:就是一次次重复的过程,这个重复的过程必须建立在每一次重复问题的复杂度都是应该下降的,直接有一个最终的结束条件(这个结束条 ...
- Mybatis+Spring实现Mysql读写分离
使用spring AbstractRoutingDatasource实现多数据源 public class DynamicDataSource extends AbstractRoutingDataS ...
- Laravel 使用 JWT 做 API 认证之tymon/jwt-auth 1.0.0-beta.1实践 - moell - SegmentFault
安装 将"tymon/jwt-auth": "1.0.0-beta.1" 添加到 composer.json 中,执行 composer update Prov ...
- 访问Bing地图
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...
- Direct2D 第2篇 绘制椭圆
原文:Direct2D 第2篇 绘制椭圆 #include <windows.h> #include <d2d1.h> #include <d2d1helper.h> ...
- Vue.之.安装
Vue.之.安装 第一步npm安装 首先:先从nodejs.org中下载nodejs 直到Finish完成安装. 打开控制命令行程序(CMD),检查是否正常 使用淘宝NPM 镜像 国内直接使用np ...
- ThinkPHP5.0中的build.php自动生成所需的目录结构的详细方法
一.来到根目录下,找到bulid.php文件进行改写. 改写方法:保留常用的目录结构,其余按照需求改吧! 二.复制一份build.php文件到application目录下 此时根目录下的bulid.p ...
- input的相关兼容性问题
近来在制作登陆页的input文本框和密码框的时候,具体的实例可参考实现带样式的表单验证,我们发现在IE下默认的情况下,input 标签的密码框和文本框宽度不一致,这就尴尬了. 解决这个办法,我们是直接 ...