python究竟要不要使用多线程
在总结concurrent.futures库之前先来弄明白三个问题:
(1)python多线程究竟有没有用?
(2)python虚拟机机制如何控制代码的执行?
(3)python中多进程处理原理是怎么样的?
1. 先来看两个例子
(1)例1
分别用单线程、使用多线程、使用多进程三种方法对最大公约数进行计算
from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
import time
a, b = pair
low = min(a, b)
for i in range(low, 0, -1):
if a % i == 0 and b % i == 0:
return i
(1963309, 2265973), (1879675, 2493670), (2030677, 3814172),
(1551645, 2229620), (1988912, 4736670), (2198964, 7876293)
if __name__ == '__main__':
# 不使用多线程和多进程
start = time.time()
results = list(map(gcd,numbers))
end = time.time()
print('未使用--timestamp:{:.3f} second'.format(end-start)) #使用多线程
start = time.time()
pool = ThreadPoolExecutor(max_workers=)
results = list(pool.map(gcd,numbers))
end = time.time()
print('使用多线程--timestamp:{:.3f} second'.format(end-start)) #使用多进程
start = time.time()
pool = ProcessPoolExecutor(max_workers=)
results = list(pool.map(gcd,numbers))
end = time.time()
print('使用多进程程--timestamp:{:.3f} second'.format(end-start))
输出:

之前线程数和进程说都为3,现在修改为4再测试

为了更能说明问题,将线程数和进程说继续增加为5

至于区别,大家自己感受,测试的条件(计算过于简单)、测试的环境都会影响测试结果
(2)例2
同样分别用单线程、使用多线程、使用多进程三种方法对网页进行爬虫,只是简单的返回status_code
from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
import time
import requests def download(url):
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:63.0) Gecko/20100101 Firefox/63.0',
'Connection':'keep-alive',
'Host':'example.webscraping.com'}
response = requests.get(url, headers=headers)
return(response.status_code) if __name__ == '__main__':
urllist = ['http://example.webscraping.com/places/default/view/Afghanistan-1',
'http://example.webscraping.com/places/default/view/Aland-Islands-2',
'http://example.webscraping.com/places/default/view/Albania-3',
'http://example.webscraping.com/places/default/view/Algeria-4',
'http://example.webscraping.com/places/default/view/American-Samoa-5'] start = time.time()
result = list(map(download, urllist))
end = time.time()
print('status_code:',result)
print('未使用--timestamp:{:.3f}'.format(end-start)) pool = ThreadPoolExecutor(max_workers = )
start = time.time()
result = list(pool.map(download, urllist))
end = time.time()
print('status_code:',result)
print('使用多线程--timestamp:{:.3f}'.format(end-start)) pool = ProcessPoolExecutor(max_workers = )
start = time.time()
result = list(pool.map(download, urllist))
end = time.time()
print('status_code:',result)
print('使用多进程程--timestamp:{:.3f}'.format(end-start))
输出:

一下就看出了区别
2. python虚拟机机制如何控制代码执行?
对于python来说,作为解释型语言,Python的解释器必须做到既安全又高效。我们都知道多线程编程会遇到的问题,解释器要留意的是避免在不同的线程操作内部共享的数据,同时它还要保证在管理用户线程时保证总是有最大化的计算资源。python是通过使用全局解释器锁来保护数据的安全性。
python 代码的执行由python虚拟机来控制,即Python先把代码(.py文件)编译成字节码(字节码在Python虚拟机程序里对应的是 PyCodeObject对象,.pyc文件是字节码在磁盘上的表现形式),交给字节码虚拟机,然后虚拟机一条一条执行字节码指令,从而完成程序的执行。 python在设计的时候在虚拟机中,同时只能有一个线程执行。同样地,虽然python解释器中可以运行多个线程,但在任意时刻,只有一个线程在解释器 中运行。而对python虚拟机的访问由全局解释器锁来控制,正是这个锁能保证同一时刻只有一个线程在运行。
在多线程的环境中,python虚拟机按一下 方式执行:
(1)设置GIL(global interpreter lock)
(2)切换到一个线程执行
(3)运行:指定数量的字节码指令、线程主动让出控制(可以调用time.sleep(0))
(4)把线程设置为睡眠状态
(5)解锁GIL
(6)再次重复以上步骤。
GIL的特性,也就导致了python不能充分利用多核cpu。而 对面向I/O的(会调用内建操作系统C代码的)程序来说,GIL会在这个I/O调用之前被释放,以允许其他线程在这个线程等待I/O的时候运行。如果线程 并未使用很多I/O操作,它会在自己的时间片一直占用处理器和GIL。
3. python多线程究竟有没有用?
通过前面的例子和python虚拟机制的理解对多线程的使用应该很清楚了,I/O密集型python程序比计算密集型的程序更能充分利用多线 程的好处。 总之,在计算密集型的程序中不要python多线程,使用python多进程进行并发编程,就不会有GIL这种问题存在,并且也能充分利用多核cpu。
(1)GIL不是bug,Guido也不是水平有限才留下这么个东西。龟叔曾经说过,尝试不用GIL而用其他的方式来做线程安全,结果python语言整体效率又下降了一倍,权衡利弊,GIL是最好的选择——不是去不掉,而是故意留着的
(2)想让python计算速度快起来,又不想写C,用pypy吧,这才是真正的大杀器
(3)可以使用协程来提高cpu的利用率,使用multiprocessing和gevent
4. python多进程执行原理
ProcessPoolExecutor类会利用multiprocessing模块所提供的底层机制,以例2作为例子描述下多进程执行流程:
(1)把urllist列表中的每一项输入数据都传给map
(2)用pickle模块对数据进行序列化,将其变成二进制形式
(3)通过本地套接字,将序列化之后的数据从解释器所在的进程发送到子解释器所在的进程
(4)在子进程中,用pickle对二进制数据进行反序列化,将其还原成python对象
(5)引入包含download函数的python模块
(6)各个子进程并行的对各自的输入数据进行计算
(7)对运行的结果进行序列化操作,将其转变成字节
(8)将这些字节通过socket复制到主进程之中
(9)主进程对这些字节执行反序列化操作,将其还原成python对象
(10)最后把每个子进程所求出的计算结果合并到一份列表之中,并返回给调用者。
multiprocessing开销比较大,原因就在于:主进程和子进程之间通信,必须进行序列化和反序列化的操作
python究竟要不要使用多线程的更多相关文章
- Python并发编程系列之多线程
1 引言 上一篇博文详细总结了Python进程的用法,这一篇博文来所以说Python中线程的用法.实际上,程序的运行都是以线程为基本单位的,每一个进程中都至少有一个线程(主线程),线程又可以创建子线程 ...
- 聊聊Python中的多进程和多线程
今天,想谈一下Python中的进程和线程. 最近在学习Django的时候,涉及到了多进程和多线程的知识点,所以想着一下把Python中的这块知识进行总结,所以系统地学习了一遍,将知识梳理如下. 1. ...
- Python爬虫进阶五之多线程的用法
前言 我们之前写的爬虫都是单个线程的?这怎么够?一旦一个地方卡到不动了,那不就永远等待下去了?为此我们可以使用多线程或者多进程来处理. 首先声明一点! 多线程和多进程是不一样的!一个是 thread ...
- python第九周:paramiko多线程、队列
1.paramiko模块 用处:连接远程服务器并执行相关操作 使用方法: SSHClient:连接远程服务器并执行基本命令 import paramiko #创建SSH对象 ssh = paramik ...
- Python+Unittest+Requests+PyMysql+HTMLReport 多线程并发接口化框架
整体框架使用的是:Python+Unittest+Requests+PyMysql+HTMLReport 多线程并发模式 主要依赖模块 Unittest.Requests.PyMysql.HTMLRe ...
- 深入浅析python中的多进程、多线程、协程
深入浅析python中的多进程.多线程.协程 我们都知道计算机是由硬件和软件组成的.硬件中的CPU是计算机的核心,它承担计算机的所有任务. 操作系统是运行在硬件之上的软件,是计算机的管理者,它负责资源 ...
- python进阶(9)多线程
什么是线程? 线程也叫轻量级进程,是操作系统能够进行运算调度的最小单位,它被包涵在进程之中,是进程中的实际运作单位.线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其 ...
- Python进阶——为什么GIL让多线程变得如此鸡肋?
本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理 做 Python 开发时,想必你肯定听过 GIL,它经常被 Python 程序员吐槽,说 Pytho ...
- Python中的多进程与多线程(一)
一.背景 最近在Azkaban的测试工作中,需要在测试环境下模拟线上的调度场景进行稳定性测试.故而重操python旧业,通过python编写脚本来构造类似线上的调度场景.在脚本编写过程中,碰到这样一个 ...
随机推荐
- Python操作SQLAlchemy
Mysql环境: MySQL 一.概述什么是数据库 ? 答:数据的仓库,如:在ATM的示例中我们创建了一个 db 目录,称其为数据库 什么是 MySQL.Oracle.SQLite.Access.MS ...
- Loadrunder脚本篇——关联数组(参数数组)
导言 前面说过可以用关联取出服务器相关的一些动态变化的信息,前面也提过web_reg_save_param中可以设置ord=all,代表从服务器中取出的是一个数组,它试用的场景是当我访问一个发帖网站, ...
- iOS KVC 和 KVO 的学习
KVC (NSKey Value Coding) :键值编码 KVO (Key Value Observing) :键值监听 前言:我曾经用过 监听 一个音频何时结束 监听视频播放 状态等 用了这种 ...
- Java字段初始化规律:
Java字段初始化规律: Java进行初始化的地方有两个,初始化块和构造函数,其中初始化块又分为静态初始化块和实例初始化块(以上程序为实例初始化块).静态初始化块是类中由static修饰的初始化块,实 ...
- 学会Retrofit+OkHttp+RxAndroid三剑客的使用,让自己紧跟Android潮流的步伐
http://blog.csdn.net/iamzgx/article/details/51607387 概括 在上一篇博客android网络框架OkHttp之get请求(源码初识) 讲解了OkHtt ...
- poj 3617输出格式问题
注意是说的80个字母一行....
- Java Comparator方法 和 Comparable接口
默认的排序方法: 让类继承Comparable接口,重写compareTo方法. 示例代码: package com.imooc.collection; import java.util.HashSe ...
- php flock 使用实例
php flock 使用实例 bool flock ( resource $handle , int $operation [, int &$wouldblock ] ) flock()允许执 ...
- Google Chrome 未响应。是否立即重新启动?
不当的退出会造成 Google Chrome 无法启动.出现“Google Chrome 未响应.是否立即重新启动?”的错误.要解决这个问题: 1.同时按住Windows旗帜键 + R键,调 ...
- DS 和【ADDRESS】学习记录
CPU要读写一个内存单元的时候,必须先给出这个内存单元的地址. 内存单元由2部分组成. 8086CPU中,内存地址由以下组成. 1:段地址 2:偏移地址 8086CPU中,有一个DS寄存器地址,通常用 ...