pyhon开多线程内存池费内存吗

中国领先的IT技术网站
51CTO旗下网站
Python 并发编程之使用多线程和多处理器
在Python编码中我们经常讨论的一个方面就是如何优化模拟执行的性能。尽管在考虑量化代码时NumPy、SciPy和pandas在这方面已然非常有用,但在构建事件驱动系统时我们无法有效地使用这些工具。有没有可以加速我们代码的其他办法?答案是肯定的,但需要留意!
作者:来源:开源中国社区 编译| 10:39
在Python编码中我们经常讨论的一个方面就是如何优化模拟执行的性能。尽管在考虑量化代码时NumPy、SciPy和pandas在这方面已然非常有用,但在构建事件驱动系统时我们无法有效地使用这些工具。有没有可以加速我们代码的其他办法?答案是肯定的,但需要留意!
在这篇文章中,我们看一种不同的模型-并发,我们可以将它引入我们Python程序中。这种模型在模拟中工作地特别好,它不需要共享状态。Monte Carlo模拟器可以用来做期权定价以及检验算法交易等类型的各种参数的模拟。
我们将特别考虑库和库。
Python并发
当Python初学者探索多线程的代码为了计算密集型优化时,问得最多的问题之一是:&当我用多线程的时候,为什么我的程序变慢了?&
在多核机器上,我们期望多线程的代码使用额外的核,从而提高整体性能。不幸的是,主Python解释器(CPython)的内部并不是真正的多线程,是通过一个全局解释锁(GIL)来进行处理的。
GIL是必须的,因为Python解释器是非线程安全的。这意味着当从线程内尝试安全的访问Python对象的时候将有一个全局的强制锁。在任何时候,仅仅一个单一的线程能够获取Python对象或者C API。每100个字节的Python指令解释器将重新获取锁,这(潜在的)阻塞了I/0操作。因为锁,CPU密集型的代码使用线程库时,不会获得性能的提高,但是当它使用多处理库时,性能可以获得提高。
并行库的实现
现在,我们将使用上面所提到的两个库来实现对一个&小&问题进行并发优化。
上面我们提到: 运行CPython解释器的Python不会支持通过多线程来实现多核处理。不过,Python确实有一个。那么如果我们(可能)不能使用多个核心进行处理,那么使用这个库能取得什么好处呢?
许多程序,尤其是那些与网络通信或者数据输入/输出(I/O)相关的程序,都经常受到网络性能或者输入/输出(I/O)性能的限制。这样Python解释器就会等待哪些从诸如网络地址或者硬盘等&远端&数据源读写数据的函数调用返回。因此这样的数据访问比从本地内存或者CPU缓冲区读取数据要慢的多。
因此,如果许多数据源都是通过这种方式访问的,那么就有一种方式对这种数据访问进行性能提高,那就是对每个需要访问的数据项都产生一个线程 。
举个例子,假设有一段Python代码,它用来对许多站点的URL进行扒取。再假定下载每个URL所需时间远远超过计算机CPU对它的处理时间,那么仅使用一个线程来实现就会大大地受到输入/输出(I/O)性能限制。
通过给每个下载资源生成一个新的线程,这段代码就会并行地对多个数据源进行下载,在所有下载都结束的时候再对结果进行组合。这就意味着每个后续下载都不会等待前一个网页下载完成。此时,这段代码就受收到客户/服务端带宽的限制。
不过,许多与财务相关的应用都受到CPU性能的限制,这是因为这样的应用都是高度集中式的对数字进行处理。这样的应用都会进行大型线性代数计算或者数值的随机统计,比如进行蒙地卡罗模拟统计。所以只要对这样的应用使用Python和全局解释锁(GIL),此时使用Python线程库就不会有任何性能的提高。
Python实现
下面这段依次添加数字到列表的&玩具&代码,举例说明了多线程的实现。每个线程创建一个新的列表并随机添加一些数字到列表中。这个已选的&玩具&例子对CPU的消耗非常高。
下面的代码概述了线程库的接口,但是他不会比我们用单线程实现的速度更快。当我们对下面的代码用多处理库时,我们将看到它会显著的降低总的运行时间。
让我们检查一下代码是怎样工作的。首先我们导入threading库。然后我们创建一个带有三个参数的函数list_append。第一个参数count定义了创建列表的大小。第二个参数id是&工作&(用于我们输出debug信息到控制台)的ID。第三个参数out_list是追加随机数的列表。
__main__函数创建了一个107的size,并用两个threads执行工作。然后创建了一个jobs列表,用于存储分离的线程。threading.Thread对象将list_append函数作为参数,并将它附加到jobs列表。
最后,jobs分别开始并分别&joined&。join()方法阻塞了调用的线程(例如主Python解释器线程)直到线程终止。在打印完整的信息到控制台之前,确认所有的线程执行完成。
&&&&&&&&&&&&&&for&i&in&range(count): &&&&&&&&&out_list.append(random.random())if&__name__&==&&__main__&: &&&&&size&=&&&&&&&&&threads&=&2&&&&&&&&&&&&&&&&&&&&&&&&jobs&=&[] &&&&&for&i&in&range(0,&threads): &&&&&&&&&out_list&=&list() &&&&&&&&&thread&=&threading.Thread(target=list_append(size,&i,&out_list)) &&&&&&&&&jobs.append(thread) &&&&&&&&&&&for&j&in&jobs: &&&&&&&&&j.start() &&&&&&&&&&&for&j&in&jobs: &&&&&&&&&j.join() &&&&&&print&&List&processing&complete.&&
我们能在控制台中调用如下的命令time这段代码
time&python&thread_test.py&
将产生如下的输出
List&processing&complete. &real&&&&0m2.003s&user&&&&0m1.838s&sys&&&&&0m0.161s&
注意user时间和sys时间相加大致等于real时间。这表明我们使用线程库没有获得性能的提升。我们期待real时间显著的降低。在并发编程的这些概念中分别被称为CPU时间和挂钟时间(wall-clock time)
多进程处理库
为了充分地使用所有现代处理器所能提供的多个核心 ,我们就要使用 。它的工作方式与线程库完全不同 ,不过两种库的语法却非常相似 。
多进程处理库事实上对每个并行任务都会生成多个操作系统进程。通过给每个进程赋予单独的Python解释器和单独的全局解释锁(GIL)十分巧妙地规避了一个全局解释锁所带来的问题。而且每个进程还可独自占有一个处理器核心,在所有进程处理都结束的时候再对结果进行重组。
不过也存在一些缺陷。生成许多进程就会带来很多I/O管理问题,这是因为多个处理器对数据的处理会引起数据混乱 。这就会导致整个运行时间增多 。不过,假设把数据限制在每个进程内部 ,那么就可能大大的提高性能 。当然,再怎么提高也不会超过所规定的极限值。
Python实现
使用Multiprocessing实现仅仅需要修改导入行和multiprocessing.Process行。这里单独的向目标函数传参数。除了这些,代码几乎和使用Threading实现的一样:
&&&&&&&&&&&&&&for&i&in&range(count): &&&&&&&&&out_list.append(random.random())if&__name__&==&&__main__&: &&&&&size&=&&&&&&&&&procs&=&2&&&&&&&&&&&&&&&&&&&&&&&&jobs&=&[] &&&&&for&i&in&range(0,&procs): &&&&&&&&&out_list&=&list() &&&&&&&&&process&=&multiprocessing.Process(target=list_append,& &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&args=(size,&i,&out_list)) &&&&&&&&&jobs.append(process) &&&&&&&&&&&for&j&in&jobs: &&&&&&&&&j.start() &&&&&&&&&&&for&j&in&jobs: &&&&&&&&&j.join() &&&&&&print&&List&processing&complete.&&
控制台测试运行时间:
time&python&multiproc_test.py&
得到如下输出:
List&processing&complete. &real&&&&0m1.045s&user&&&&0m1.824s&sys&&&&&0m0.231s&
在这个例子中可以看到user和sys时间基本相同,而real下降了近两倍。之所以会这样是因为我们使用了两个进程。扩展到四个进程或者将列表的长度减半结果如下(假设你的电脑至少是四核的):
List&processing&complete. &real&&&&0m0.540s&user&&&&0m1.792s&sys&&&&&0m0.269s&
使用四个进程差不多提高了3.8倍速度。但是,在将这个规律推广到更大范围,更复杂的程序上时要小心。数据转换,硬件cacha层次以及其他一些问题会减弱加快的速度。
在下一篇文章中我们会将Event-Driben Basketer并行化,从而提高其运行多维参数寻优的能力。
相关阅读:
英文原文:
译文链接:
【编辑推荐】
【责任编辑: TEL:(010)】
大家都在看猜你喜欢
头条原创原创原创专题
24H热文一周话题本月最赞
讲师:1人学习过
讲师:33人学习过
讲师:0人学习过
精选博文论坛热帖下载排行
本书紧紧围绕“软件架构设计”这一主题,立足实践解析了软件架构的概念,阐述了切实可行的软件架构设计方法,提供了可操作性极强的完整的架...
订阅51CTO邮刊购买商品:
课程名称读取中
支付方式:
微信支付宝
请扫码二维码进行支付
商品价格:价格读取中
成功后会自动跳转,请勿在支付过程中关闭本窗口!
请扫码二维码进行支付
商品价格:价格读取中
成功后会自动跳转,请勿在支付过程中关闭本窗口!
由百度云提供技术支持
&学院APP&&
扫描微信二维码精彩活动、课程更新抢先知
下载客户端,离线视频任您学
1.&01 python s8day7 课前牛B分享
2.&02 python s8day7 上节内容回顾
3.&03 python s8day7 多进程的使用
4.&04 python s8day7 子进程与父进程的关系
5.&05 python s8day7 进程间的内存同步方法queue
6.&06 python s8day7 进程间的内存同步方法manager
7.&07 python s8day7 通过Pool产生多进程
8.&08 python s8day7 通过Pool产生多进程2
9.&09 python s8day7 开发审计堡垒机
10.&10 开发审计堡垒机修改paramiko源码记录操作
11.&11 python s8day7 审计堡垒机的安全控制
12.&12 python s8day7 Select-Poll-epoll介绍
13.&13 python s8day7 Select代码实例解析
14.&14 python s8day7 作业之审计作业
【课程类型】实战教学
【难度级别】高级
【适合人群】所有人
【课程介绍】 从0基础开始讲解Python语法、流程控制、函数式编程、面向对象开发等最重要的Python开发必备基础知识。由老男孩教育重磅推出,必是精品,不容错过。
【课程目标】 使学员掌握Python开发基础知识,能够开发常用的Python脚本
【课程计划】 day7 Python自动化开发基础 多线程\多进程\及主机管理
已更新完毕
全部评价(1)
Disallowed Key Characters.,你妈!!!
38课程241749学员
所属机构:博客访问: 663321
博文数量: 89
博客积分: 1216
博客等级: 少尉
技术积分: 1994
注册时间:
优秀是一种习惯
IT168企业级官微
微信号:IT168qiye
系统架构师大会
微信号:SACC2013
分类: Python/Ruby
& & & &&threading提供了一个比thread模块更高层的API来提供线程的并发性。这些线程并发运行并共享内存。&
&&&&& &&下面来看threading模块的具体用法:&
& & &一、Thread的使用
目标函数可以实例化一个Thread对象,每个Thread对象代表着一个线程,可以通过start()方法,开始运行。
& & &这里对使用多线程并发,和不适用多线程并发做了一个比较:
首先是不使用多线程的操作:
代码如下:
#!/usr/bin/python
#compare for multi threads
import time
def worker():
print "worker"
time.sleep(1)
if __name__ == "__main__":
for i in xrange(5):
执行结果如下:
下面是使用多线程并发的操作:
代码如下:
#!/usr/bin/python
import threading
import time
def worker():
print "worker"
time.sleep(1)
for i in xrange(5):
t = threading.Thread(target=worker)
可以明显看出使用了多线程并发的操作,花费时间要短的很多。
二、threading.activeCount()的使用,此方法返回当前进程中线程的个数。返回的个数中包含主线程。
代码如下:
#!/usr/bin/python
#current's number of threads
import threading
import time
def worker():
print "test"
time.sleep(1)
for i in xrange(5):
t = threading.Thread(target=worker)
print "current has %d threads" % (threading.activeCount() - 1)
三、threading.enumerate()的使用。此方法返回当前运行中的Thread对象列表。
代码如下:
#!/usr/bin/python
#test the variable threading.enumerate()
import threading
import time
def worker():
print "test"
time.sleep(2)
threads = []
for i in xrange(5):
t = threading.Thread(target=worker)
threads.append(t)
for item in threading.enumerate():
print item
for item in threads:
print item
四、threading.setDaemon()的使用。设置后台进程。
代码如下:
#!/usr/bin/python
#create a daemon
import threading
import time
def worker():
time.sleep(3)
print "worker"
t=threading.Thread(target=worker)
t.setDaemon(True)
print "haha"
可以看出worker()方法中的打印操作并没有显示出来,说明已经成为后台进程。
阅读(101922) | 评论(1) | 转发(4) |
相关热门文章
给主人留下些什么吧!~~
不错!!!
请登录后评论。Python全栈开发之11、进程和线程 | 开源社区
您的赞赏,是对我创作的最大鼓励。
分享到微信
扫码分享到微信
剔透沉静,敏学优思,淡泊而明事理。
爱编程,爱生活,爱分享
689篇作品527.1k阅读总量}

我要回帖

更多关于 多线程共享内存 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信