python多线程学习(一)
python多线程、多进程 初探
原先刚学Java的时候,多线程也学了几天,后来一直没用到。然后接触python的多线程的时候,貌似看到一句”python多线程很鸡肋“,于是乎直接跳过了多线程的学习。
接触爬虫,才开始用到多进程这个东西。
既然用到了,就系统地学吧。先来python的,再总结一下Java的。
什么是线程和进程
很经典的一个解释是“进程是资源分配的最小单位,线程是CPU调度的最小单位“。
比如我们在任务管理器中看到的就是进程,例如qq.exe,qq就是一个进程。打开qq后,启动多条线程,各自负责文件下载、更新朋友圈、聊条等功能。
每个进程,都有自己的地址空间、内存、数据栈以及其他用于跟踪执行的辅助数据。进程可以通过派生(fork或spawn)新的进程来执行其他任务,不过这个新的进程是独立的,所有进程之间只能采用IPC(进程间通信)的方式共享信息。
线程,于进程类似,有时候也称为轻量级进程,可以把他们 视为一个主进程中并行运行的一些”迷你进程“。同一个进程下的线程共享资源。
以前单核CPU时代,多线程用处并不多。进程和线程没法做到真正的并行运算,而如今多核时代,真正能实现多条线程一起跑。
线程于进程的对比
| 对比维度 | 进程 | 线程 |
|---|---|---|
| 数据共享、同步 | IPC共享数据较复杂,同步简单 | 同进程下线程数据共享,但同步复杂 |
| 可靠性 | 进程间互相独立 | 一个线程坏掉可能使整个进程挂掉 |
| 资源消耗 | 独立资源,消耗多。进程间切换慢 | 切换快,共享资源,资源消耗少 |
总结,进程和线程还可以类比为火车和车厢:
线程在进程下行进(单纯的车厢无法运行)
一个进程可以包含多个线程(一辆火车可以有多个车厢)
不同进程间数据很难共享(一辆火车上的乘客很难换到另外一辆火车,比如站点换乘)
同一进程下不同线程间数据很易共享(A车厢换到B车厢很容易)
进程要比线程消耗更多的计算机资源(采用多列火车相比多个车厢更耗资源)
进程间不会相互影响,一个线程挂掉将导致整个进程挂掉(一列火车不会影响到另外一列火车,但是如果一列火车上中间的一节车厢着火了,将影响到该趟火车的所有车厢)
进程可以拓展到多机,进程最多适合多核(不同火车可以开在多个轨道上,同一火车的车厢不能在行进的不同的轨道上)
进程使用的内存地址可以上锁,即一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。(比如火车上的洗手间)-”互斥锁(mutex)”
进程使用的内存地址可以限定使用量(比如火车上的餐厅,最多只允许多少人进入,如果满了需要在门口等,等有人出来了才能进去)-“信号量(semaphore)”
p ython的多线程为何鸡肋
因为GIL,全局解释锁机制,python的多线程是伪多线程。
准确来说,GIL并不是python的机制,而是CPython解释器(主流解释器)的机制(比如Jpython就没有GIL)。
什么是GIL
其实有点类似于单核CPU执行多进程的过程,虽然可以有多个进程,但只有一个CPU啊,一个进程执行的时候别的进程只能等,只是快速切换执行感觉上是一起执行罢了。现在多核CPU能做到真正的并行
在 Cpython 解释器(Python语言的主流解释器)中,有一把全局解释锁(Global Interpreter Lock),在解释器解释执行 Python 代码时,先要得到这把锁。锁只有一把,一个线程执行的时候,别的线程也得等,多核CPU也没用。(真正的多线程,比如Java的多线程,在多核情况下,可以实现真正的多条线程同步运行)
1.设置GIL
2.切换到一个线程去执行
3.运行
指定数量的字节码指令
线程主动让出控制(可以调用time.sleep(0))
4.把线程设置完睡眠状态
5.解锁GIL
6.再次重复以上步骤
线程什么时候会让出全局锁呢?一是执行满 100 tick(可以理解为100单位的指令),二是遇见了阻塞(比如I/O)。
考虑两种情况
- 计算密集:这时,几乎没一个线程都是执行满100tick后让出锁的,所以和单线程跑没啥区别,而且线程切换也需要时间,最终程序运行时间比起单线程反而更长。
- I/O密集型:一个线程只执行了几tick,遇见I/O阻塞。这时候代码已经解释过了,可以独立执行I/O过程,这是切换到另一个线程,同理。最终类似于真正的多线程,在I/O上节省的时间多余在线程切换消耗的时间,比单线程执行快。
因而,python多线程适合I/O密集型程序。
CPython 为什么要这样设计
多线程有个问题,怎么解决共享数据的同步、一致性问题?因为,对于多个线程访问共享数据时,可能有两个线程同时修改一个数据情况,如果没有合适的机制保证数据的一致性,那么程序最终导致异常,所以,Python之父就搞了个全局的线程锁,不管你数据有没有同步问题,反正一刀切,上个全局锁,保证数据安全,简单粗暴。
这种解决办法放在90年代,其实是没什么问题的,毕竟,那时候的硬件配置还很简陋,单核 CPU 还是主流,多线程的应用场景也不多,大部分时候还是以单线程的方式运行,单线程不要涉及线程的上下文切换,效率反而比多线程更高(在多核环境下,不适用此规则)。所以,采用 GIL 的方式来保证数据的一致性和安全,未必不可取,至少在当时是一种成本很低的实现方式。
解决方案
因为Cpython的GIL,使python的多线程显得鸡肋。可以试着使用别的解释器,但更常见的作法还是使用多进程。(当然,多进程必然消耗资源多一点)。
接下来学习
多线程主要用的是标准库中的threading包,thread基本不用(主要是它在主线程结束后,其他线程会直接停止,有点类似于守护进程,显然不安全)。
多进程使用标准库中的multiprocessing。
接下来几天阅读官方文档中的这几节。
python多线程学习(一)的更多相关文章
- python多线程学习记录
1.多线程的创建 import threading t = t.theading.Thread(target, args--) t.SetDeamon(True)//设置为守护进程 t.start() ...
- python 多线程学习小记
python对于thread的管理中有两个函数:join和setDaemon setDaemon:如果在程序中将子线程设置为守护线程,则该子线程会在主线程结束时自动退出,设置方式为thread.set ...
- python多线程学习二
本文希望达到的目标: 多线程同步原语:互斥锁 多线程队列queue 线程池threadpool 一.多线程同步原语:互斥锁 在多线程代码中,总有一些特定的函数或者代码块不应该被多个线程同时执行,通常包 ...
- Python多线程学习
一.Python中的线程使用: Python中使用线程有两种方式:函数或者用类来包装线程对象. 1. 函数式:调用thread模块中的start_new_thread()函数来产生新线程.如下例: ...
- python 多线程学习
多线程(multithreaded,MT),是指从软件或者硬件上实现多个线程并发执行的技术 什么是进程? 计算机程序只不过是磁盘中可执行的二进制(或其他类型)的数据.它们只有在被读取到内存中,被操作系 ...
- Python 多线程学习(转)
转自:http://www.cnblogs.com/slider/archive/2012/06/20/2556256.html 引言 对于 Python 来说,并不缺少并发选项,其标准库中包括了对线 ...
- Python多线程学习资料1
一.Python中的线程使用: Python中使用线程有两种方式:函数或者用类来包装线程对象. 1. 函数式:调用thread模块中的start_new_thread()函数来产生新线程.如下例: ...
- Python多线程学习笔记
Python中与多线程相关的模块有 thread, threading 和 Queue等,thread 和threading模块允许程序员创建和管理线程.thread模块提供了基本的线程和锁的支持,而 ...
- 《转》Python多线程学习
原地址:http://www.cnblogs.com/tqsummer/archive/2011/01/25/1944771.html 一.Python中的线程使用: Python中使用线程有两种方式 ...
随机推荐
- java中jsp的EL的定义以及使用
1.定义: EL(Expression Language) 是为了使JSP写起来更加简单.表达式语言的灵感来自于 ECMAScript 和 XPath 表达式语言,它提供了在 JSP 中简化表达式的方 ...
- Android 内存 - 获取单个应用内存限制
方法一: adb shell getprop | grep dalvik.vm.heapgrowthlimit [dalvik.vm.heapgrowthlimit]: [64m] 方法二: Acti ...
- Linux下SHA256校验
一.将Hash: SHA256文件和需要检验的文件放在同一个文件夹内 二.$sha256sum -c SHA265 文件 输出: 校验文件:ok
- AcWing:149. 荷马史诗(哈夫曼编码 + k叉哈夫曼树)
追逐影子的人,自己就是影子. ——荷马 达达最近迷上了文学. 她喜欢在一个慵懒的午后,细细地品上一杯卡布奇诺,静静地阅读她爱不释手的<荷马史诗>. 但是由<奥德赛>和<伊 ...
- vue的通信方式(二)---祖父孙三个级别的之间的隔代通信
在之前的文章中我们提到了vue常用的几种通信方式,如父子,子父,以及兄弟组件之间的通信,可以通过这个传送门了解他们:Vue通信方式(一) 当我们如果遇到祖组件,父组件,孙组件,三个级别嵌套时,我们该怎 ...
- 踩坑:VScode 集成 eslint 插件
本文以 Vue 官方脚手架 Vue-cli 为例: 1. 创建 Vue 项目 注意:Vue-cli 默认给出了 eslint 配置,一路回车即可.最后在安装模块的时候,选择直接安装!我用淘宝镜像安装时 ...
- docker启动常见报错
Docker启动时的报错汇总 22017.11.10 16:30:29字数 575阅读 27184 八个Docker常见故障 https://mp.weixin.qq.com/s/2GNKmRJtBG ...
- Android jni/ndk编程四:jni引用类型
一.JNI引用类型 JNI支持三种类型的 opaque reference:local references, global references,和weak global references,下面 ...
- LNMPA是什么?
也许大家对LAMP.LNMP比较熟悉,LAMP代表Linux下Apache.MySQL.PHP这种网站服务器架构:LNMP代表的是Linux下Nginx.MySQL.PHP这种网站服务器架构.LNMP ...
- Unix介绍
1965年,AT&T贝尔电话实验室.通用电气公司.麻省理工学院MAC课题组一起联合开发一个称为Multics的新操作系统.该项目目的是让大型主机可以同时提供300台以上的终端机连接使用.其被设 ...