如何在Python中编写并发程序

作者&投稿:焦贺 (若有异议请与网页底部的电邮联系)
如何优雅的编写Python并发程序~

在Python中,由于历史原因(GIL),使得Python中多线程的效果非常不理想.GIL使得任何时刻Python只能利用一个CPU核,并
且它的调度算法简单粗暴:多线程中,让每个线程运行一段时间t,然后强行挂起该线程,继而去运行其他线程,如此周而复始,直到所有线程结束.

这使得无法有效利用计算机系统中的"局部性",频繁的线程切换也对缓存不是很友好,造成资源的浪费.

据说Python官方曾经实现了一个去除GIL的Python解释器,但是其效果还不如有GIL的解释器,遂放弃.后来Python官方推出了"利
用多进程替代多线程"的方案,在Python3中也有concurrent.futures这样的包,让我们的程序编写可以做到"简单和性能兼得".

多进程/多线程+Queue

一般来说,在Python中编写并发程序的经验是:计算密集型任务使用多进程,IO密集型任务使用多进程或者多线程.另外,因为涉及到资源共享,所
以需要同步锁等一系列麻烦的步骤,代码编写不直观.另外一种好的思路是利用多进程/多线程+Queue的方法,可以避免加锁这样麻烦低效的方式.

现在在Python2中利用Queue+多进程的方法来处理一个IO密集型任务.

假设现在需要下载多个网页内容并进行解析,单进程的方式效率很低,所以使用多进程/多线程势在必行.

#下面是一个示例,我写了一个简单的for循环,并加入了多线程并发。# -*- coding:utf-8 -*-import thread,threading#Test Functiondef ForTest(): for i in range(10): print i class mythread(threading.Thread): def __init__(self,threadname): threading.Thread.__init__(self) def run(self): lock.acquire() for j in xrange(int(times)): #Add Own Fuction Here ForTest() lock.release() def MutiThread(num,times): threads=[] global ft for x in xrange(num): threads.append(mythread(num)) for t in threads: lock.acquire() t.start() lock.release() for t in threads: t.join()if __name__ == '__main__': global num,times,lock num=2 #num 并发数 times=2 #times 运行次数 lock=threading.Lock() MutiThread(num,times)运行结果:
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9

GIL

在Python中,由于历史原因(GIL),使得Python中多线程的效果非常不理想.GIL使得任何时刻Python只能利用一个CPU核,并
且它的调度算法简单粗暴:多线程中,让每个线程运行一段时间t,然后强行挂起该线程,继而去运行其他线程,如此周而复始,直到所有线程结束.

这使得无法有效利用计算机系统中的"局部性",频繁的线程切换也对缓存不是很友好,造成资源的浪费.

据说Python官方曾经实现了一个去除GIL的Python解释器,但是其效果还不如有GIL的解释器,遂放弃.后来Python官方推出了"利
用多进程替代多线程"的方案,在Python3中也有concurrent.futures这样的包,让我们的程序编写可以做到"简单和性能兼得".

多进程/多线程+Queue

一般来说,在Python中编写并发程序的经验是:计算密集型任务使用多进程,IO密集型任务使用多进程或者多线程.另外,因为涉及到资源共享,所
以需要同步锁等一系列麻烦的步骤,代码编写不直观.另外一种好的思路是利用多进程/多线程+Queue的方法,可以避免加锁这样麻烦低效的方式.

现在在Python2中利用Queue+多进程的方法来处理一个IO密集型任务.

假设现在需要下载多个网页内容并进行解析,单进程的方式效率很低,所以使用多进程/多线程势在必行.
我们可以先初始化一个tasks队列,里面将要存储的是一系列dest_url,同时开启4个进程向tasks中取任务然后执行,处理结果存储在一个results队列中,最后对results中的结果进行解析.最后关闭两个队列.

下面是一些主要的逻辑代码.

# -*- coding:utf-8 -*-

#IO密集型任务
#多个进程同时下载多个网页
#利用Queue+多进程
#由于是IO密集型,所以同样可以利用threading模块

import multiprocessing

def main():
tasks = multiprocessing.JoinableQueue()
results = multiprocessing.Queue()
cpu_count = multiprocessing.cpu_count() #进程数目==CPU核数目

create_process(tasks, results, cpu_count) #主进程马上创建一系列进程,但是由于阻塞队列tasks开始为空,副进程全部被阻塞
add_tasks(tasks) #开始往tasks中添加任务
parse(tasks, results) #最后主进程等待其他线程处理完成结果

def create_process(tasks, results, cpu_count):
for _ in range(cpu_count):
p = multiprocessing.Process(target=_worker, args=(tasks, results)) #根据_worker创建对应的进程
p.daemon = True #让所有进程可以随主进程结束而结束
p.start() #启动

def _worker(tasks, results):
while True: #因为前面所有线程都设置了daemon=True,故不会无限循环
try:
task = tasks.get() #如果tasks中没有任务,则阻塞
result = _download(task)
results.put(result) #some exceptions do not handled
finally:
tasks.task_done()

def add_tasks(tasks):
for url in get_urls(): #get_urls() return a urls_list
tasks.put(url)

def parse(tasks, results):
try:
tasks.join()
except KeyboardInterrupt as err:
print "Tasks has been stopped!"
print err

while not results.empty():
_parse(results)

if __name__ == '__main__':
main()

利用Python3中的concurrent.futures包

在Python3中可以利用concurrent.futures包,编写更加简单易用的多线程/多进程代码.其使用感觉和Java的concurrent框架很相似(借鉴?)
比如下面的简单代码示例

def handler():
futures = set()

with concurrent.futures.ProcessPoolExecutor(max_workers=cpu_count) as executor:
for task in get_task(tasks):
future = executor.submit(task)
futures.add(future)

def wait_for(futures):
try:
for future in concurrent.futures.as_completed(futures):
err = futures.exception()
if not err:
result = future.result()
else:
raise err
except KeyboardInterrupt as e:
for future in futures:
future.cancel()
print "Task has been canceled!"
print e
return result

总结

要是一些大型Python项目也这般编写,那么效率也太低了.在Python中有许多已有的框架使用,使用它们起来更加高效.


在Python中使用Asyncio系统(3-4)​Task 和 Future
它满足函数的类型签名(因为Task是Future的子类),但从Python 3.8开始,我们不再允许在Task上调用set_result():尝试这样做将引发RuntimeError。这个想法是,一个Task代表一个正在运行的协程,所以结果应该总是来自于task自身。 (L10, L24)但是,我们仍然可以cancel()一个任务,它将在底层协程中引发CancelledError。 Creat...

python 怎么搜索字典里的值并且打印出来
连接的那台打印机的电脑 打开控制面板,找到打印机,右键共享 ,第一次共享会出来一个框,你点击网络安装向导——下一步(工作组名就选择那个不要改)——提示你启用文件与打印机共享圆圈中点击下——完成该向导——电脑重启

python stackless 怎么多线程并发
微进程是stackless的基本构成单元,你可以通过提供任一个Python可调用对象(通常为函数或类的方法)来建立它,这将建立一个微进程并将其添加到调度器。这是一个快速演示: Python 2.4.3 Stackless 3.1b3 060504 (#69, May 3 2006, 19:20:41) [MSC v.1310 32 bit (Intel)] on win32 Type "help", "copyrigh...

如何在Notepad++中配置Python开发环境
cmd \/k python "$(FULL_CURRENT_PATH)" & ECHO. & PAUSE & EXIT cmd \/k python: 表示打开Cmd窗口,运行\/k后边的命令,并且执行完毕后保留窗口。此处即python(因为在环境变量里已经添加了Python目录,所以这里不用指定Python程序的目录,就可直接找到)(FULL_CURRENT_PATH) :Notepad++的宏定义,...

.Python编程与图像识别课程在调压器上有什么应用?
并且可以将其传送到远程,方便工程师们进行相关监测;图像识别与嵌入式相结合,也是当今比较。流行的一个方向。 Python对于嵌入式的支持也比较好,你可以将嵌入式与图像识别和相关的使用场景,就比如本题当中所提到的结合起来。这样就会有更大的应用范围。以上是一些基础应用,希望可以帮助到你。

最近python很火,职场人士真的有必要去学一下吗?
根据自己需求来决定,如果没有基础,想要转行做编程工作可以学习python,因为python要比其他语言更加简单,容易入门,适合初学者学习;如果是相关职场人员,也有必要学习python,python可以简化我们的工作,提高效率,毕竟技多不压身。

Ruby为什么比Python成功
1. 比Perl更强大,比Python更面向对象 “比Perl更强大,比Python更面向对象”,这是Ruby创始人Matz设计Ruby的初衷。Python既支持面向过程的编程也支持面向对象的编程,而Ruby则是完全面向对象。在Ruby中,任何东西都是对象,包括Python中的基本数据类型;每个过程或函数都是方法。例如,取-3的绝对值,在...

奈学教育-P7大数据架构师5期
大数据的价值日益凸显,影响着各行各业,如电商精准营销、企业资源管理等。最后,大数据与云计算的关系密不可分,大数据是资产,云计算是挖掘工具。两者相互促进,大数据开发者需要掌握的语言如Java和Python是基础,而面对就业市场挑战,参加高薪计划需要具备编程基础、对学习的热爱和足够的时间投入。

编程5分钟,命名2小时!大神程序员都在用这套命名方法
对于经常在C++、Java、Python等主流语言上切换的强迫症来说,换个语言换种命名风格简直不要太混乱。 既然有这么多命名要做,不妨做好它。本期内容中,异步君为大家带来了起个好名字应遵从的几条简单规则,一起来看看吧 — 01 — 名副其实 名副其实说起来简单。我们想要强调,这事很严肃。选个好名字要花时间...

Ruby,Perl,Python,Lua等语言相互之间都有哪些异同点
本文从RoR对Ruby的影响、Ruby的优势等多个角度分析了Ruby比Python成功的原因。伴随着RoR的风行,Ruby语言受到越来越多的开发者的关注,同为脚本语言,Python的地位却略显尴尬,什么样的原因,造成了这样的局面?笔者认为有以下几个方面:一、RoR的推波助澜笔者认为,Ruby的成功,很大一部分是由于RoR的带动...

泸西县17228707588: 如何在Python中编写并发程序 -
官急迪先: GIL 在Python中,由于历史原因(GIL),使得Python中多线程的效果非常不理想.GIL使得任何时刻Python只能利用一个CPU核,并 且它的调度算法简单粗暴:多线程中,让每个线程运行一段时间t,然后强行挂起该线程,继而去运行其他线程,...

泸西县17228707588: 如何使用Python实现并发编程 -
官急迪先: 多线程几乎是每一个程序猿在使用每一种语言时都会首先想到用于解决并发的工具(JS程序员请回避),使用多线程可以有效的利用CPU资源(Python例外).然而多线程所带来的程序的复杂度也不可避免,尤其是对竞争资源的同步问题.然...

泸西县17228707588: Python并发编程之创建多线程的几种方法 -
官急迪先: Django: Py Web应用开发框架 Diesel:基于Greenlet的事件I/O框架 Flask:一个用Py编写的轻量级Web应用框架 Cubes:轻量级Py OLAP框架 Kartograph.py:创造矢量地图的轻量级Py框架 Pulsar:Py的事件驱动并发框架 Web2py:全栈式Web...

泸西县17228707588: python如何在单机上实现web服务器最大并发 -
官急迪先: 必须对你每天都用的底层的软件系统有进一步的理解,包括编程语言、编译器和解释器、数据库和操作系统、WEB服务器和WEB框架.为了更好更深入的理解这些系统,你可以从零开始一块砖地,一面墙地,重建它们

泸西县17228707588: python多线程与多进程的概念与区别 -
官急迪先: 1.什么是线程?线程是操作系统能够进行运算调度的最小单位(程序执行流的最小单元).它被包含在进程之中,是进程中的实际运作单位.一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同...

泸西县17228707588: python yield怎么实现并发 -
官急迪先: 生成器是通过一个或多个yield表达式构成的函数,每一个生成器都是一个迭代器(但是迭代器不一定是生成器).如果一个函数包含yield关键字,这个函数就会变为一个生成器.

泸西县17228707588: 怎样用python写代码 -
官急迪先: 可以点击"File"-"New File",在弹出来的窗口中写代码,完成后按F5或点击"Run"-"Run Module"运行.

泸西县17228707588: 使用Python编程 -
官急迪先: symbol = {'+', '-', '*', '/', '%'} s = input('请输入算式:') if len(symbol - set(s)) < 5: try: print("{0} = {1}".format(s, eval(s))) except ZeroDivisionError: print('除数不能为 0 ') except: print('error') else: print('error')

泸西县17228707588: python多进程和多线程究竟谁更快 -
官急迪先: 你要知道一点python的多线程是伪多线程,对于io密集型代码,效果还好.但是python多进程是充分利用cpu的 不存在谁更快,看怎么搭配使用

泸西县17228707588: 如何用python写一段代码? -
官急迪先: 简单的,可以使用python 的CGI模块,需要你的服务器开启CGI支持.网页内容如下:<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>python cgi</title> </head><body><p style="font-size:24pt...

本站内容来自于网友发表,不代表本站立场,仅表示其个人看法,不对其真实性、正确性、有效性作任何的担保
相关事宜请发邮件给我们
© 星空见康网