本文是如何《优雅地实现Python通用多线程/进程并行模块》的后续。因为我发现,自认为懂了一点多线程开发的皮毛,写了那么个multi_helper的玩意儿,后来才发现我靠原来就是一坨屎。自己辛苦开发的并行库,在Python的原生类库中就有了优雅地多的实现。并且还有更优雅的asyncio库!这简直让人累觉不爱。

首先,并行和并发是不同的。100个进程可以在1个CPU上并发地执行,但却能在4个CPU上并行地执行。这就能看出两者的不同。

在实际编程中,有大量的计算或IO是一下子无法结束的。那么如何充分利用各种资源呢?有两个流派:

  • 多进程,多线程(并行)
  • 协程 (并发)

本文实在不能算完整,但汇集了笔者觉得很精髓的不少内容,因此可以认为是一篇综述性文章。

多线程和多进程

先说前者,定义本身是常识就不多说。初级程序员为了让程序变快,于是就狂开线程,不论是IO操作还是计算,能开100个就绝不开50个。线程回收一律不管,这程序只能是玩具。

不过,众所周知Python有GIL(全局解释器锁),任何时候都只有一个线程在工作。那多线程加速还玩个屁啊?!龟叔站出来了,他说:

  • Python在IO操作时,会自动释放GIL,因此IO密集型程序的多线程是有意义的
  • 即使不能多线程,还能多进程,这才是核武器。而Python有非常好用的multiprocessing库解决进程间协同问题。

然而,这些耗时任务,如果不需要知道结果,那还好说(很多程序员直接就把结果写到不同的文件里去,从而避免此问题,太low了)。可是每次都写文件再读出来这太麻烦了。如何收集计算结果呢?

笔者那个工具的做法,是将结果保存到一个队列中,从外部对其进行消费。但Python官方库早就有类似的实现了,多线程和多进程版本分别叫ThreadPoolExecutorProcessPoolExecutor。至于怎么用,出门问度娘。老司机在文章里贴代码太没有逼格了。

好,你度娘上完了。我们会发现他们都在concurrent.futures库里。这个表述很有意思。这是未来才能知道结果的任务,因此叫future。 在《Python高手之路》的第14章里,这样的概念叫期物。你如果你要获取某个任务的result,就必须等待该任务完成。也可以创建任务的数组,然后批量去执行它。类似的还有C#的async关键字以及task的result属性。

让我觉得比较神奇的是,Python的python-parallelize库,就能隐含地将for循环转换为并行for循环,代码更加简洁。这才是多进程的终极神器。写法如下:

import os
from parallelize import parallelize for i in parallelize(range(100)):
print(os.getpid(), i)

你要做的,仅仅是在迭代器外面套上套子,其他并行的操作就都由它执行完了。你猜这个库的核心代码有多少行?30行不到!它用os.fork创建出很多子进程,之后将不同的任务分配到这些进程上。而代码编写和串行程序几乎没有区别。

对多数人来说,高阶函数本来就比较反人类。这样写下来反而会清爽很多。值得学习!这部分写完了,我们进入协程的神秘世界。

asyncio 和 yield from

协程(coroutine)是比线程更轻量级的概念。大家会好奇协程的本质,因为当年学C语言时可是完全没这个东西啊。协程说白了就是语法糖。编译器会把协程的一坨东西整合到大的状态机里,类似于一堆switch-case和while的代码里,从而实现无缝调度。但对代码编写者来说,同一语义的依然能在同一个代码位置,从而更好理解。

yield是Python中的关键字。我们熟悉的是它的迭代生成器用法。更高阶的则是协程。例如:

a=yield b

如果你认为a=b,那就大错特错了。a是外部send函数的返回值。至于更详细的信息,可参考

http://www.jianshu.com/p/d09778f4e055

到了Python3, 更丧心病狂的yield from横空出世。最浅显的理解,是下面的代码:

def merge():
yield from 'ABC'
yield from '123'

于是在merge执行循环时,A,B,C,1,2,3会依次返回。那么肯定会有人纳闷,为何不直接itertools.chain呢?因为yield from有更复杂的功能,即能够让外部的generator对内部做消息传递。yield from 就像一个套子,无缝地将生成器merge了起来。

更详细的信息,可参考

http://blog.theerrorlog.com/yield-from-in-python-3.html

它有什么用呢?asyncio.

如果你喜欢C#的await和async关键字,则一定会对它的表现印象深刻。异步函数再也不用像js那样做疯狂的回调,顿时代码清晰了很多。这些也是编译器大人的功劳。

详细的可参考:

http://blog.jobbole.com/63897/

看得其实还是晕乎乎的,尽管我自认为对yield和协程有了相对充分的了解却依然如此。

结语

大神说,不要重复造轮子。你会发现之前想做的所有事情,都有远比你想的优雅的多的实现

不过问题在于,用了协程,yield from, asyncio,别人就看不懂我写的代码了,并且不得不升级到py3,可维护性顿时下降很多。因为我发现,就我身边会python的人来说,熟悉且能灵活使用以上那些语义的人不到百分之一。

有任何问题,随时交流。

Python 多线程进程高级指南(二)的更多相关文章

  1. Python 多线程、多进程 (二)之 多线程、同步、通信

    Python 多线程.多进程 (一)之 源码执行流程.GIL Python 多线程.多进程 (二)之 多线程.同步.通信 Python 多线程.多进程 (三)之 线程进程对比.多线程 一.python ...

  2. python进阶-------进程线程(二)

    Python中的进程线程(二) 一.python中的"锁" 1.GIL锁(全局解释锁) 含义: Python中的线程是操作系统的原生线程,Python虚拟机使用一个全局解释器锁(G ...

  3. Python多线程&进程

    一.线程&进程 对于操作系统来说,一个任务就是一个进程(Process),比如打开一个浏览器就是启动一个浏览器进程,打开一个记事本就启动了一个记事本进程,打开两个记事本就启动了两个记事本进程, ...

  4. python多线程和多进程(二)

    ---恢复内容开始--- 一.多进程 1.multiprocessing模块用来开启子进程,并在子进程中执行我们定制的任务(比如函数),该模块与多线程模块threading的编程接口类似. impor ...

  5. Python 多线程 进程与线程相关概念 (一)

    0x00 并行和并发 并行:同时做某些事,可以互不干扰的同一时刻做几件事. 并发:也是同时做某些事,但是强调,同一时刻做了几件事. 0x01 并发的解决: 1)队列.缓冲区: 排队就是队列,先进先出. ...

  6. Python 多线程、多进程 (三)之 线程进程对比、多进程

    Python 多线程.多进程 (一)之 源码执行流程.GIL Python 多线程.多进程 (二)之 多线程.同步.通信 Python 多线程.多进程 (三)之 线程进程对比.多线程 一.多线程与多进 ...

  7. Python 多线程和线程池

    一,前言 进程:是程序,资源集合,进程控制块组成,是最小的资源单位 特点:就对Python而言,可以实现真正的并行效果 缺点:进程切换很容易消耗cpu资源,进程之间的通信相对线程来说比较麻烦 线程:是 ...

  8. Python 多线程、多进程 (一)之 源码执行流程、GIL

    Python 多线程.多进程 (一)之 源码执行流程.GIL Python 多线程.多进程 (二)之 多线程.同步.通信 Python 多线程.多进程 (三)之 线程进程对比.多线程 一.python ...

  9. Python 多线程、进程、协程上手体验

    浅谈 Python 多线程.进程.协程上手体验 前言:浅谈 Python 很多人都认为 Python 的多线程是垃圾(GIL 说这锅甩不掉啊~):本章节主要给你体验下 Python 的两个库 Thre ...

随机推荐

  1. 前端学习记录之Javascript-DOM

    简介 为了快捷方便,在工作和项目中,一直都是使用的框架,比如jqeruy,vuejs,react等.然后在长时间使用框架后,往往有一个后遗症,就是对于原生的Javascript基本上已经不知道怎么写了 ...

  2. C# 串口接收数据中serialPort.close()死锁

    最近在做一个有关高铁模拟仓显示系统的客户端程序,在这个程序中要运用串口serialPort传输数据,因为每次接收数据结束后要更新UI界面,所以就用到了的Invoke,将更新UI的程序代码封装到一个方法 ...

  3. JavaWeb框架SSH_Struts2_(三)

    1. Struts2的拦截器(使用拦截器实现权限控制) 拦截器简介 拦截器概述 拦截器工作原理 拦截器的配置 Struts2的内建拦截器 内建拦截器的介绍 内建拦截器的配置 自定义拦截器 实现自定义拦 ...

  4. 题目1083:特殊乘法-九度oj

    题目描述: 写个算法,对2个小于1000000000的输入,求结果. 特殊乘法举例:123 * 45 = 1*4 +1*5 +2*4 +2*5 +3*4+3*5 输入: 两个小于1000000000的 ...

  5. ChatterBot之linux下安装mongodb 02

    当前环境 :centos 6.9 mongodb版本 mongodb-linux-x86_64-3.4.4.tgz 使用链接工具:studio-3t-x64.msi.zip 首先我们先来安装mongo ...

  6. unity中调用其他脚本函数的方法(小白之路)

    第一种,被调用脚本函数为static类型,调用时直接用  脚本名.函数名().很不实用-- 第二种,GameObject.Find("脚本所在物体名").SendMessage(& ...

  7. SEO中TDK写法的意思以及注意事项

    在SEO中,所谓的TDK其实就是title.description.keywords这三个标签,这三个标签在网站的优化过程中,至关重要所以今天童童来和大家分享下,如何去写好TDK标签! 1.title ...

  8. python 小白(无编程基础,无计算机基础)的开发之路,辅助知识6 python字符串/元组/列表/字典互转

    神奇的相互转换,小白同学可以看看,很有帮助 #1.字典dict = {'name': 'Zara', 'age': 7, 'class': 'First'} #字典转为字符串,返回:<type ...

  9. SpringCache与redis集成,优雅的缓存解决方案

    缓存可以说是加速服务响应速度的一种非常有效并且简单的方式.在缓存领域,有很多知名的框架,如EhCache .Guava.HazelCast等.Redis作为key-value型数据库,由于他的这一特性 ...

  10. CSS的常见问题

    1.css的编码风格 多行式:可读性越强,但是CSS文件的行数过多,影响开发速度,增大CSS文件的大小 一行式:可读性稍差,有效减少CSS文件的行数,有利于提高开发速度,减小CSS文件的大小 2.id ...