在总结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. 联合文件系统 unionfs

  2. Matlab命令合集 妈妈再也不用担心我不会用matlab了

    matlab命令 一.常用对象操作:除了一般windows窗口的常用功能键外.1.!dir 可以查看当前工作目录的文件. !dir& 可以在dos状态下查看.2.who 可以查看当前工作空间变 ...

  3. php获取MAC地址

    /** *获取mac地址 **/ class GetMacAddr{ var $return_array = array(); // 返回带有MAC地址的字串数组 var $mac_addr; fun ...

  4. ES6 随记(3.1)-- 字符串的拓展

    上一章请见: 1. ES6 随记(1)-- let 与 const 2. ES6 随记(2)-- 解构赋值 4. 拓展 a. 字符串的拓展 有些字符需要 4 个字节储存,比如 \uD83D\uDE80 ...

  5. INSPIRED启示录 读书笔记 - 第35章 情感接纳曲线

    技术接纳曲线 涉及了技术创新者.尝鲜者.早期消费大众.后期消费大众和跟随者,很少有产品能越过鸿沟——获得尝鲜者以外消费者的青睐 不同类型的用户具有不同的情感需求,除了技术接纳曲线模型描述用户外,还应该 ...

  6. 由于ptrace.h文件导致的内核编译出错的解决方法

    arch/x86/kernel/ptrace.c:1472:17: error: conflicting types for 'syscall_trace_enter'  In file includ ...

  7. tyvj 1057 金明的预算方案 背包dp

    P1057 金明的预算方案 时间: 1000ms / 空间: 131072KiB / Java类名: Main 背景 NOIP2006 提高组 第二道 描述 金明今天很开心,家里购置的新房就要领钥匙了 ...

  8. 【转载】JAVA中线程的两种实现方法-实现Runnable接口和继承Thread类

    转自: http://blog.csdn.net/sunguangran/article/details/6069317 非常感谢原作者,整理的这么详细. 在java中可有两种方式实现多线程,一种是继 ...

  9. JDBC批量插入blob数据

    图片从接口读取后是base64的字符串,所以转成byte数组进行保存. 我们一般保存数据的话,都是基本数据,对于这些图片数据大部分会将图片保存成Blob,Clob等. Blob存储的是二进制对象数据( ...

  10. js简单工厂

    我以计算器为例写一个简单工厂模式,只完成加减乘除4个计算功能,考虑到其他功能方便日后扩展,遵循开放-封闭原则. 简单工厂类图: 先看一下C#的简单工厂是如何实现的: 定义抽象类Operation,加减 ...