如何在Python中编写并发程序


Posted in Python onFebruary 27, 2016

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 相关文章推荐
Python实现设置windows桌面壁纸代码分享
Mar 28 Python
pandas.DataFrame 根据条件新建列并赋值的方法
Apr 08 Python
Python将多个list合并为1个list的方法
Jun 27 Python
使用 Python 实现简单的 switch/case 语句的方法
Sep 17 Python
Python自动发送邮件的方法实例总结
Dec 08 Python
Python3列表内置方法大全及示例代码小结
May 10 Python
多版本python的pip 升级后, pip2 pip3 与python版本失配解决方法
Sep 11 Python
基于pytorch 预训练的词向量用法详解
Jan 06 Python
Python实现投影法分割图像示例(一)
Jan 17 Python
PyTorch中Tensor的数据类型和运算的使用
Sep 03 Python
python用tkinter实现一个简易能进行随机点名的界面
Sep 27 Python
Python根据URL地址下载文件并保存至对应目录的实现
Nov 15 Python
Python 多线程抓取图片效率对比
Feb 27 #Python
Python 的描述符 descriptor详解
Feb 27 #Python
简析Python的闭包和装饰器
Feb 26 #Python
Android应用开发中Action bar编写的入门教程
Feb 26 #Python
12步教你理解Python装饰器
Feb 25 #Python
Python实现字典依据value排序
Feb 24 #Python
Python中方法链的使用方法
Feb 23 #Python
You might like
paypal即时到账php实现代码
2010/11/28 PHP
Zend studio文件注释模板设置方法
2013/09/29 PHP
php技术实现加载字体并保存成图片
2015/07/27 PHP
smarty高级特性之对象的使用方法
2015/12/25 PHP
教你在header中隐藏php的版本信息
2016/08/10 PHP
laravel数据库查询结果自动转数组修改实例
2021/02/27 PHP
些很实用且必用的小脚本代码
2006/06/26 Javascript
Extjs学习笔记之六 面版
2010/01/08 Javascript
javascript中callee与caller的用法和应用场景
2010/12/08 Javascript
web开发人员学习jQuery的6大理由及jQuery的优势介绍
2013/01/03 Javascript
封装好的一个万能检测表单的方法
2015/01/21 Javascript
JS实现仿腾讯微博无刷新删除微博效果代码
2015/10/16 Javascript
js多功能分页组件layPage使用方法详解
2016/05/19 Javascript
js日期相关函数dateAdd,dateDiff,dateFormat等介绍
2016/09/24 Javascript
vue实现的上拉加载更多数据/分页功能示例
2019/05/25 Javascript
微信js-sdk 录音功能的示例代码
2019/11/01 Javascript
用Golang运行JavaScript的实现示例
2019/11/25 Javascript
Python中文件遍历的两种方法
2014/06/16 Python
Python2.x版本中cmp()方法的使用教程
2015/05/14 Python
python中根据字符串调用函数的实现方法
2016/06/12 Python
一些常用的Python爬虫技巧汇总
2016/09/28 Python
Python实现PS滤镜特效之扇形变换效果示例
2018/01/26 Python
Python生成器generator用法示例
2018/08/10 Python
python+mysql实现个人论文管理系统
2019/10/25 Python
python 实现全球IP归属地查询工具
2020/12/18 Python
css3实现冲击波效果的示例代码
2018/01/11 HTML / CSS
Converse匡威法国官网:美国著名帆布鞋品牌
2018/12/05 全球购物
库存图片、照片、矢量图、视频和音乐:Shutterstock
2021/02/12 全球购物
瑞士首家网上药店折扣店:McDrogerie
2020/12/22 全球购物
会计专业大学生职业生涯规划书
2014/02/11 职场文书
法人任命书范本
2014/06/04 职场文书
责任书范本
2014/08/25 职场文书
大学三好学生主要事迹范文
2015/11/03 职场文书
基于python的matplotlib制作双Y轴图
2021/04/20 Python
关于Javascript闭包与应用的详解
2021/04/22 Javascript
Python自动化之批量处理工作簿和工作表
2021/06/03 Python