如何在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在校内发人人网状态(人人网看状态)
Feb 19 Python
详细介绍Ruby中的正则表达式
Apr 10 Python
win7+Python3.5下scrapy的安装方法
Jul 31 Python
Django添加favicon.ico图标的示例代码
Aug 07 Python
对python dataframe逻辑取值的方法详解
Jan 30 Python
Python实现的栈、队列、文件目录遍历操作示例
May 06 Python
使用python代码进行身份证号校验的实现示例
Nov 21 Python
Python使用turtle库绘制小猪佩奇(实例代码)
Jan 16 Python
浅析Django 接收所有文件,前端展示文件(包括视频,文件,图片)ajax请求
Mar 09 Python
Django 再谈一谈json序列化
Mar 16 Python
详解pandas.DataFrame.plot() 画图函数
Jun 14 Python
python re模块常见用法例举
Mar 01 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
聊天室php&mysql(六)
2006/10/09 PHP
php过滤html标记属性类用法实例
2014/09/23 PHP
PHP程序员必须清楚的问题汇总
2014/12/18 PHP
Yii中使用PHPExcel导出Excel的方法
2014/12/26 PHP
php使用Jpgraph创建柱状图展示年度收支表效果示例
2017/02/15 PHP
Laravel + Elasticsearch 实现中文搜索的方法
2020/02/02 PHP
firefox火狐浏览器与与ie兼容的2个问题总结
2010/07/20 Javascript
jQuery照片伸缩效果不影响其他元素的布局
2014/05/09 Javascript
jQuery扁平化风格下拉框美化插件FancySelect使用指南
2015/02/10 Javascript
jQuery 移动端拖拽(模块化开发,触摸事件,webpack)
2016/10/28 Javascript
详解angularjs popup-table 弹出框表格指令
2017/09/20 Javascript
jQuery创建及操作xml格式数据示例
2018/05/26 jQuery
微信小程序项目实践之主页tab选项实现
2018/07/18 Javascript
js+html5实现手机九宫格密码解锁功能
2018/07/30 Javascript
详解vue3.0 diff算法的使用(超详细)
2020/07/01 Javascript
在Python中使用列表生成式的教程
2015/04/27 Python
基于python yield机制的异步操作同步化编程模型
2016/03/18 Python
深入学习Python中的装饰器使用
2016/06/20 Python
使用Python实现博客上进行自动翻页
2017/08/23 Python
python如何爬取个性签名
2018/06/19 Python
Python绘制频率分布直方图的示例
2019/07/08 Python
python爬虫解决验证码的思路及示例
2019/08/01 Python
python3 assert 断言的使用详解 (区别于python2)
2019/11/27 Python
python实现视频读取和转化图片
2019/12/10 Python
python argparse模块通过后台传递参数实例
2020/04/20 Python
Django实现celery定时任务过程解析
2020/04/21 Python
纽约JewelryAffairs珠宝店:精细金银时尚首饰
2017/02/05 全球购物
娇韵诗俄罗斯官方网站:Clarins俄罗斯
2020/10/03 全球购物
实习教师个人的自我评价
2013/11/08 职场文书
求职简历中自我评价
2014/01/28 职场文书
工程资料员岗位职责
2014/03/10 职场文书
学习全国两会精神心得体会范文
2014/03/17 职场文书
交通事故赔偿起诉书
2015/05/20 职场文书
教师工作证明范本
2015/06/12 职场文书
2015大学迎新标语
2015/07/16 职场文书
使用python求解迷宫问题的三种实现方法
2022/03/17 Python