Python并行编程(十):多线程性能评估
1、基本概念
GIL是CPython解释器引入的锁,GIL在解释器层面阻止了真正的并行运行。解释器在执行任何线程之前,必须等待当前正在运行的线程释放GIL,事实上,解释器会强迫想要运行的线程必须拿到GIL才能访问解释器的任何资源,例如栈或Python对象等,这也正是GIL的目的,为了阻止不同的线程并发访问Python对象。这样GIL可以保护解释器的内存,让垃圾回收工作正常。但事实上,这却造成了程序员无法通过并行执行多线程来提高程序的性能。如果我们去掉GIL,就可以实现真正的并行。GIL并没有影响多处理器并行的线程,只是限制了一个解释器只能有一个线程在运行。
2、测试用例
测试一:空函数
from threading import Thread def function_to_run():
pass class threads_object(Thread):
def run(self):
function_to_run() class nothreads_object(object):
def run(self):
function_to_run() def non_threaded(num_iter):
funcs = []
for i in range(int(num_iter)):
funcs.append(nothreads_object())
for i in funcs:
i.run() def threaded(num_threads):
funcs = []
for i in range(int(num_threads)):
funcs.append(threads_object())
for i in funcs:
i.start()
for i in funcs:
i.join() def show_results(func_name, results):
print("%-23s %4.6f seconds" % (func_name, results)) if __name__ == "__main__":
import sys
from timeit import Timer
repeat = 100
number = 1
num_threads = [1, 2, 4, 8]
print('starting tests')
for i in num_threads:
t = Timer("non_threaded(%s)" % i, "from __main__ import non_threaded")
best_result = min(t.repeat(repeat=repeat, number=number))
show_results("non_threaded (%s iters)" % i, best_result)
t = Timer("threaded(%s)" % i, "from __main__ import threaded")
best_result = min(t.repeat(repeat=repeat, number=number))
show_results("threaded (%s threads)" % i, best_result)
print('Iterations complete')
下面的代码是用来评估多线程应用性能的简单代码。每一次测试都循环调用函数100次,重复执行多次,取速度最快的一次。在for循环中,调用non_threaded和threaded函数。同时,我们会不断增加调用次数和线程数来重复执行这个测试。在非线程测试中,调用函数与定义线程数一样多的次数。只需改变function_to_run的内容即可进行测试。
上面代码测试的为空函数,执行结果如下:

通过结果发现,使用线程的开销比不使用线程的开销大得多。
测试二:数字处理
将function_to_run改成计算斐波那契数列
def function_to_run():
# pass
a, b = 0, 1
for i in range(10000):
a, b = b, a + b
结果如下:

结果:提高线程的数量并没有带来收益,因为GIL和线程管理代码的开销,多线程运行永远不可能比函数顺序执行更快。GIL只允许解释器一次执行一个线程。
测试三:数据读取
更改function_to_run如下:
def function_to_run():
# pass
# a, b = 0, 1
# for i in range(10000):
# a, b = b, a + b fh = open("README.md","rb")
size = 1024
for i in range(1000):
fh.read(size)
运行结果:

测试四:URL请求
from threading import Thread def function_to_run():
# pass
# a, b = 0, 1
# for i in range(10000):
# a, b = b, a + b # fh = open("README.md","rb")
# size = 1024
# for i in range(1000):
# fh.read(size)
import urllib.request
for i in range(10):
with urllib.request.urlopen("https://www.baidu.com/") as f:
f.read(1024) class threads_object(Thread):
def run(self):
function_to_run() class nothreads_object(object):
def run(self):
function_to_run() def non_threaded(num_iter):
funcs = []
for i in range(int(num_iter)):
funcs.append(nothreads_object())
for i in funcs:
i.run() def threaded(num_threads):
funcs = []
for i in range(int(num_threads)):
funcs.append(threads_object())
for i in funcs:
i.start()
for i in funcs:
i.join() def show_results(func_name, results):
print("%-23s %4.6f seconds" % (func_name, results)) if __name__ == "__main__":
import sys
from timeit import Timer
repeat = 100
number = 1
num_threads = [1, 2, 4, 8]
print('starting tests')
for i in num_threads:
t = Timer("non_threaded(%s)" % i, "from __main__ import non_threaded")
best_result = min(t.repeat(repeat=repeat, number=number))
show_results("non_threaded (%s iters)" % i, best_result)
t = Timer("threaded(%s)" % i, "from __main__ import threaded")
best_result = min(t.repeat(repeat=repeat, number=number))
show_results("threaded (%s threads)" % i, best_result)
print('Iterations complete')
运行结果:

在有I/O操作时,多线程比单线程快得多。增加线程并不会提高应用启动的时间,但是可以支持并发。例如,一次性创建一个线程池,并重用worker会很有用,这可以让我们切分一个大的数据集,用同样的函数处理不同的部分。
Python并行编程(十):多线程性能评估的更多相关文章
- Python并行编程(十四):异步编程
1.基本概念 除了顺序执行和并行执行的模型以外,还有异步模型,这是事件驱动模型的基础.异步活动的执行模型可以只有一个单一的主控制流,能在单核心系统和多核心系统中运行. 在并发执行的异步模型中,许多任务 ...
- Python并行编程(十二):进程同步
1.基本概念 多个进程可以协同工作来完成一项任务,通常需要共享数据.所以在多进程之间保持数据的一致性就很重要,需要共享数据协同的进程必须以适当的策略来读写数据.同步原语和线程的库类似. - Lock: ...
- Python并发编程04 /多线程、生产消费者模型、线程进程对比、线程的方法、线程join、守护线程、线程互斥锁
Python并发编程04 /多线程.生产消费者模型.线程进程对比.线程的方法.线程join.守护线程.线程互斥锁 目录 Python并发编程04 /多线程.生产消费者模型.线程进程对比.线程的方法.线 ...
- Python并行编程的几个要点
一.基于线程的并行编程 如何使用Python的线程模块 如何定义一个线程 如何探测一个线程 如何在一个子类中使用线程 Lock和RLock实现线程同步 信号实现线程同步 条件(condition)实现 ...
- Python并行编程(二):基于线程的并行
1.介绍 软件应用中使用最广泛的并行编程范例是多线程.通常一个应用有一个进程,分成多个独立的线程,并行运行.互相配合,执行不同类型的任务. 线程是独立的处理流程,可以和系统的其他线程并行或并发地执行. ...
- python并行编程
一.编程思想 并行编程的思想:分而治之,有两种模型 1.MapReduce:将任务划分为可并行的多个子任务,每个子任务完成后合并得到结果 例子:统计不同形状的个数. 先通过map进行映射到多个子任务, ...
- python并行编程学习之绪论
计算机科学的研究,不仅应该涵盖计算处理所基于的原理,还因该反映这些领域目前的知识状态.当今,计算机技术要求来自计算机科学所有分支的专业人员理解计算机处理的基础的关键,在于知道软件和硬件在所有层面上的交 ...
- 第十篇.4、python并发编程之多线程
一 threading模块介绍 multiprocess模块的完全模仿了threading模块的接口,二者在使用层面,有很大的相似性,因而不再详细介绍 官网链接:https://docs.python ...
- Python并发编程之多线程使用
目录 一 开启线程的两种方式 二 在一个进程下开启多个线程与在一个进程下开启多个子进程的区别 三 练习 四 线程相关的其他方法 五 守护线程 六 Python GIL(Global Interpret ...
随机推荐
- JS学习笔记(3)--json格式数据的添加,删除及排序方法
这篇文章主要介绍了json格式数据的添加,删除及排序方法,结合实例形式分析了针对一维数组与二维数组的json格式数据进行增加.删除与排序的实现技巧,需要的朋友可以参考下 本文实例讲述了json格式 ...
- mr中间结果优化
转载请注明出处:http://blog.csdn.net/lastsweetop/article/details/9187721 作为输入 当压缩文件做为mapreduce的输入时,mapreduce ...
- mysql_windows解压包安装
WIN下安装64位的解压版mysql-5.6.24-winx64 参考如下安装步骤: 1.将解压缩后的文件放到自己想要的地方 并配置环境变量. 示例中存放的目录为:D:\Program Files\m ...
- PostgreSQL入门指南
默认端口:5432 创建用户与数据库 PostgreSQL初次安装后,默认会生成一个名为postgres的数据库和一个名为postgres的数据库用户.这里需要注意的是,同时还生成了一个名为postg ...
- java 基础数据结构
数据结构, 需要考虑两个方面: 1. 每个元素具体的存储方法 (java中是一个对象) 2. 元素之间的关系如何实现存储 (java中也是一个对象) 另外在java中, 已经可以把跟数据结构有关的一些 ...
- 打开palette控制面板
(2)
- selenium:chromedriver与chrome版本的对应关系
转自:http://blog.csdn.NET/huilan_same/article/details/51896672 再使用selenium打开chrome浏览器的时候,需要用chromedriv ...
- 函数 free 的原型
函数 free 的原型如下: void free( void * memblock ); 为什么 free 函数不象 malloc 函数那样复杂呢? 这是因为指针 p 的类型以及它所指 的内存的容量事 ...
- 让rm命令提示确认后再删除
首先在~/.bashrc文件中添加一行: # User specific aliases and functionsalias rm='rm -i' 注意,此处 rm 和 = 之间不能有空格,否则会有 ...
- gcc参数PIE和PIC的区别和共同点
gcc参数的PIE和PIC 区别 PIE:用在可执行文件 PIC:用在库文件 共同点 生成跟位置没有关系的symbol