如何在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编程实现删除VC临时文件及Debug目录的方法
Mar 22 Python
python实现淘宝秒杀聚划算抢购自动提醒源码
Jun 23 Python
用tensorflow搭建CNN的方法
Mar 05 Python
tensorflow 1.0用CNN进行图像分类
Apr 15 Python
Python不使用int()函数把字符串转换为数字的方法
Jul 09 Python
python3利用tcp实现文件夹远程传输
Jul 28 Python
pyinstaller打包多个py文件和去除cmd黑框的方法
Jun 21 Python
python使用beautifulsoup4爬取酷狗音乐代码实例
Dec 04 Python
django实现将修改好的新模型写入数据库
Mar 31 Python
Pytorch 高效使用GPU的操作
Jun 27 Python
基于django2.2连oracle11g解决版本冲突的问题
Jul 02 Python
python3爬虫GIL修改多线程实例讲解
Nov 24 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中$this->含义分析
2009/11/29 PHP
测试php函数的方法
2013/11/13 PHP
php获取数组元素中头一个数组元素值的实现方法
2014/12/20 PHP
Yii2 RESTful中api的使用及开发实例详解
2016/07/06 PHP
IE与FireFox的兼容性问题分析
2007/04/22 Javascript
NodeJS框架Express的模板视图机制分析
2011/07/19 NodeJs
JavaScript中的Math 使用介绍
2014/04/21 Javascript
用jquery.sortElements实现table排序
2014/05/04 Javascript
jquery滚动加载数据的方法
2015/03/09 Javascript
jQuery实现为控件添加水印文字效果(附源码)
2015/12/02 Javascript
js微信分享API
2020/10/11 Javascript
jQuery实现复选框的全选和反选
2017/02/02 Javascript
javascript自执行函数
2017/02/10 Javascript
js获取当前页的URL与window.location.href简单方法
2017/02/13 Javascript
vue2.0数据双向绑定与表单bootstrap+vue组件
2017/02/27 Javascript
vuejs开发组件分享之H5图片上传、压缩及拍照旋转的问题处理
2017/03/06 Javascript
详解javascript中的变量提升和函数提升
2018/05/24 Javascript
解决vue的变量在settimeout内部效果失效的问题
2018/08/30 Javascript
ES6实现图片切换特效代码
2020/01/14 Javascript
[51:00]Secret vs VGJ.S 2018国际邀请赛淘汰赛BO3 第一场 8.24
2018/08/25 DOTA
Python只用40行代码编写的计算器实例
2017/05/10 Python
详解Python可视化神器Yellowbrick使用
2019/11/11 Python
OpenCV python sklearn随机超参数搜索的实现
2020/01/17 Python
python实现贪吃蛇双人大战
2020/04/18 Python
python+appium+yaml移动端自动化测试框架实现详解
2020/11/24 Python
python可视化分析的实现(matplotlib、seaborn、ggplot2)
2021/02/03 Python
python上下文管理的使用场景实例讲解
2021/03/03 Python
加拿大当代时尚服饰、配饰和鞋类专业零售商和制造商:LE CHÂTEAU
2017/10/06 全球购物
白兰氏健康Mall:BRAND’S
2017/11/13 全球购物
出门问问全球官方商城:Tichome音箱和TicWatch智能手表
2017/12/02 全球购物
国际礼品店:GiftsnIdeas
2018/05/03 全球购物
说一下Linux下有关用户和组管理的命令
2014/08/18 面试题
四年的个人工作自我评价
2013/12/10 职场文书
运动会闭幕式通讯稿
2015/07/18 职场文书
python实现大文本文件分割成多个小文件
2021/04/20 Python
手把手教你从零开始react+antd搭建项目
2021/06/03 Javascript