在总结concurrent.futures库之前先来弄明白三个问题:   

(1)python多线程究竟有没有用?

(2)python虚拟机机制如何控制代码的执行?

(3)python中多进程处理原理是怎么样的?

1. 先来看两个例子

(1)例1

  分别用单线程、使用多线程、使用多进程三种方法对最大公约数进行计算

from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
import time
  def gcd(pair):
    a, b = pair
    low = min(a, b)
    for i in range(low, 0, -1):
        if a % i == 0 and b % i == 0:
            return i

 numbers = [
    (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究竟要不要使用多线程的更多相关文章

  1. Python并发编程系列之多线程

    1 引言 上一篇博文详细总结了Python进程的用法,这一篇博文来所以说Python中线程的用法.实际上,程序的运行都是以线程为基本单位的,每一个进程中都至少有一个线程(主线程),线程又可以创建子线程 ...

  2. 聊聊Python中的多进程和多线程

    今天,想谈一下Python中的进程和线程. 最近在学习Django的时候,涉及到了多进程和多线程的知识点,所以想着一下把Python中的这块知识进行总结,所以系统地学习了一遍,将知识梳理如下. 1. ...

  3. Python爬虫进阶五之多线程的用法

    前言 我们之前写的爬虫都是单个线程的?这怎么够?一旦一个地方卡到不动了,那不就永远等待下去了?为此我们可以使用多线程或者多进程来处理. 首先声明一点! 多线程和多进程是不一样的!一个是 thread ...

  4. python第九周:paramiko多线程、队列

    1.paramiko模块 用处:连接远程服务器并执行相关操作 使用方法: SSHClient:连接远程服务器并执行基本命令 import paramiko #创建SSH对象 ssh = paramik ...

  5. Python+Unittest+Requests+PyMysql+HTMLReport 多线程并发接口化框架

    整体框架使用的是:Python+Unittest+Requests+PyMysql+HTMLReport 多线程并发模式 主要依赖模块 Unittest.Requests.PyMysql.HTMLRe ...

  6. 深入浅析python中的多进程、多线程、协程

    深入浅析python中的多进程.多线程.协程 我们都知道计算机是由硬件和软件组成的.硬件中的CPU是计算机的核心,它承担计算机的所有任务. 操作系统是运行在硬件之上的软件,是计算机的管理者,它负责资源 ...

  7. python进阶(9)多线程

    什么是线程? 线程也叫轻量级进程,是操作系统能够进行运算调度的最小单位,它被包涵在进程之中,是进程中的实际运作单位.线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其 ...

  8. Python进阶——为什么GIL让多线程变得如此鸡肋?

    本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理 做 Python 开发时,想必你肯定听过 GIL,它经常被 Python 程序员吐槽,说 Pytho ...

  9. Python中的多进程与多线程(一)

    一.背景 最近在Azkaban的测试工作中,需要在测试环境下模拟线上的调度场景进行稳定性测试.故而重操python旧业,通过python编写脚本来构造类似线上的调度场景.在脚本编写过程中,碰到这样一个 ...

随机推荐

  1. dom树改变监听

    function unwrap(el, target) { if ( !target ) { target = el.parentNode; } while (el.firstChild) { tar ...

  2. 转:ADO,OLEDB,ODBC,DAO的区别

    ODBC(Open Database Connectivity,开放数据库互连) 1992年,微软公司开放服务结构(WOSA,Windows Open Services Architecture)中有 ...

  3. php token 生成

    php token的生成   接口特点汇总: 1.因为是非开放性的,所以所有的接口都是封闭的,只对公司内部的产品有效: 2.因为是非开放性的,所以OAuth那套协议是行不通的,因为没有中间用户的授权过 ...

  4. mybatis使用注意的细节

    1.mybatis对sql执行后会对结果进行封装,如果没有返回任何记录,只是封装后的对象没有值,而对象并不为空null: (这个问题疏忽坑了两次,在对返回数组结果进行判断的时候,我用的if(Array ...

  5. python函数模拟mysql增删改查功能

    import os list1 = ['staff_id', 'name', 'age', 'phone', 'dept', 'enroll_date'] def staff_info(): #获取员 ...

  6. Shell编程之循环控制及状态返回值

    1.break.continue.exit.return的对比 break.continue在条件语句和循环语句中用于控制程序走向: exit用于终止所有语句并退出当前脚本,还可以返回上一次程序或命令 ...

  7. body-parser小解

    body-parser用来解析http请求体,对不同的content-type有不同的处理方式, 首先介绍一下常见的四种Content-Type: 1.application/x-www-form-u ...

  8. linux usb简介

    参考书:<linux device drivers>.<usb 2.0规范> <usb3.1规范><usb白皮书> 以linux为例来说明usb系统. ...

  9. 重用UITableViewCell对象的概念

    重用UITableViewCell对象 UITableView控件十分常见,基本上我们随意打开一款App都能见到,它被用来列表展示数据,而其中的每一行内容都是一个cell对象 我们知道手机设备上的内存 ...

  10. VMware ESXI 6.5安装教程

    设置从IPMI Virtual Disk 3000启动,出现如下界面: 默认选择第一项,回车安装 安装程序正在检测服务器硬件信息,如果不满足系统安装条件会跳出错误提示. 检测完成之后会出现下面界面 回 ...