python并发编程之多进程、多线程、异步和协程详解


Posted in Python onOctober 28, 2016

最近学习python并发,于是对多进程、多线程、异步和协程做了个总结。
一、多线程

多线程就是允许一个进程内存在多个控制权,以便让多个函数同时处于激活状态,从而让多个函数的操作同时运行。即使是单CPU的计算机,也可以通过不停地在不同线程的指令间切换,从而造成多线程同时运行的效果。

多线程相当于一个并发(concunrrency)系统。并发系统一般同时执行多个任务。如果多个任务可以共享资源,特别是同时写入某个变量的时候,就需要解决同步的问题,比如多线程火车售票系统:两个指令,一个指令检查票是否卖完,另一个指令,多个窗口同时卖票,可能出现卖出不存在的票。

在并发情况下,指令执行的先后顺序由内核决定。同一个线程内部,指令按照先后顺序执行,但不同线程之间的指令很难说清除哪一个会先执行。因此要考虑多线程同步的问题。同步(synchronization)是指在一定的时间内只允许某一个线程访问某个资源。

1、thread模块

2、threading模块
threading.Thread 创建一个线程。

给判断是否有余票和卖票,加上互斥锁,这样就不会造成一个线程刚判断没有余票,而另外一个线程就执行卖票操作。

#! /usr/bin/python
#-* coding: utf-8 -*
# __author__ ="tyomcat"
import threading
import time
import os

def booth(tid):
  global i
  global lock
  while True:
    lock.acquire()
    if i!=0:
      i=i-1
      print "窗口:",tid,",剩余票数:",i
      time.sleep(1)
    else:
      print "Thread_id",tid,"No more tickets"
      os._exit(0)
    lock.release()
    time.sleep(1)

i = 100
lock=threading.Lock()

for k in range(10):

  new_thread = threading.Thread(target=booth,args=(k,))
  new_thread.start()

二、协程(又称微线程,纤程)

协程,与线程的抢占式调度不同,它是协作式调度。协程也是单线程,但是它能让原来要使用异步+回调方式写的非人类代码,可以用看似同步的方式写出来。

1、协程在python中可以由生成器(generator)来实现。

首先要对生成器和yield有一个扎实的理解.

调用一个普通的python函数,一般是从函数的第一行代码开始执行,结束于return语句、异常或者函数执行(也可以认为是隐式地返回了None)。

一旦函数将控制权交还给调用者,就意味着全部结束。而有时可以创建能产生一个序列的函数,来“保存自己的工作”,这就是生成器(使用了yield关键字的函数)。

能够“产生一个序列”是因为函数并没有像通常意义那样返回。return隐含的意思是函数正将执行代码的控制权返回给函数被调用的地方。而"yield"的隐含意思是控制权的转移是临时和自愿的,我们的函数将来还会收回控制权。

看一下生产者/消费者的例子:

#! /usr/bin/python
#-* coding: utf-8 -*
# __author__ ="tyomcat"
import time
import sys
# 生产者
def produce(l):
  i=0
  while 1:
    if i < 10:
      l.append(i)
      yield i
      i=i+1
      time.sleep(1)
    else:
      return   
# 消费者
def consume(l):
  p = produce(l)
  while 1:
    try:
      p.next()
      while len(l) > 0:
        print l.pop()
    except StopIteration:
      sys.exit(0)
if __name__ == "__main__":
  l = []
  consume(l)

当程序执行到produce的yield i时,返回了一个generator并暂停执行,当我们在custom中调用p.next(),程序又返回到produce的yield i 继续执行,这样 l 中又append了元素,然后我们print l.pop(),直到p.next()引发了StopIteration异常。

2、Stackless Python

3、greenlet模块

基于greenlet的实现则性能仅次于Stackless Python,大致比Stackless Python慢一倍,比其他方案快接近一个数量级。其实greenlet不是一种真正的并发机制,而是在同一线程内,在不同函数的执行代码块之间切换,实施“你运行一会、我运行一会”,并且在进行切换时必须指定何时切换以及切换到哪。

4、eventlet模块

三、多进程
1、子进程(subprocess包)

在python中,通过subprocess包,fork一个子进程,并运行外部程序。

调用系统的命令的时候,最先考虑的os模块。用os.system()和os.popen()来进行操作。但是这两个命令过于简单,不能完成一些复杂的操作,如给运行的命令提供输入或者读取命令的输出,判断该命令的运行状态,管理多个命令的并行等等。这时subprocess中的Popen命令就能有效的完成我们需要的操作

>>>import subprocess
>>>command_line=raw_input()
ping -c 10 www.baidu.com
>>>args=shlex.split(command_line)
>>>p=subprocess.Popen(args)

利用subprocess.PIPE将多个子进程的输入和输出连接在一起,构成管道(pipe):

import subprocess
child1 = subprocess.Popen(["ls","-l"], stdout=subprocess.PIPE)
child2 = subprocess.Popen(["wc"], stdin=child1.stdout,stdout=subprocess.PIPE)
out = child2.communicate()
print(out)

communicate() 方法从stdout和stderr中读出数据,并输入到stdin中。

2、多进程(multiprocessing包)

(1)、multiprocessing包是Python中的多进程管理包。与threading.Thread类似,它可以利用multiprocessing.Process对象来创建一个进程。

进程池 (Process Pool)可以创建多个进程。

apply_async(func,args)  从进程池中取出一个进程执行func,args为func的参数。它将返回一个AsyncResult的对象,你可以对该对象调用get()方法以获得结果。

close()  进程池不再创建新的进程

join()   wait进程池中的全部进程。必须对Pool先调用close()方法才能join。

#! /usr/bin/env python
# -*- coding:utf-8  -*-
# __author__ == "tyomcat"
# "我的电脑有4个cpu"

from multiprocessing import Pool
import os, time

def long_time_task(name):
  print 'Run task %s (%s)...' % (name, os.getpid())
  start = time.time()
  time.sleep(3)
  end = time.time()
  print 'Task %s runs %0.2f seconds.' % (name, (end - start))

if __name__=='__main__':
  print 'Parent process %s.' % os.getpid()
  p = Pool()
  for i in range(4):
    p.apply_async(long_time_task, args=(i,))
  print 'Waiting for all subprocesses done...'
  p.close()
  p.join()
  print 'All subprocesses done.'

(2)、多进程共享资源

通过共享内存和Manager对象:用一个进程作为服务器,建立Manager来真正存放资源。

其它的进程可以通过参数传递或者根据地址来访问Manager,建立连接后,操作服务器上的资源。

#! /usr/bin/env python
# -*- coding:utf-8  -*-
# __author__ == "tyomcat"

from multiprocessing import Queue,Pool
import multiprocessing,time,random

def write(q):

  for value in ['A','B','C','D']:
    print "Put %s to Queue!" % value
    q.put(value)
    time.sleep(random.random())


def read(q,lock):
  while True:
    lock.acquire()
    if not q.empty():
      value=q.get(True)
      print "Get %s from Queue" % value
      time.sleep(random.random())
    else:
      break
    lock.release()

if __name__ == "__main__":
  manager=multiprocessing.Manager()
  q=manager.Queue()
  p=Pool()
  lock=manager.Lock()
  pw=p.apply_async(write,args=(q,))
  pr=p.apply_async(read,args=(q,lock))
  p.close()
  p.join()
  print
  print "所有数据都写入并且读完"

四、异步

无论是线程还是进程,使用的都是同步进制,当发生阻塞时,性能会大幅度降低,无法充分利用CPU潜力,浪费硬件投资,更重要造成软件模块的铁板化,紧耦合,无法切割,不利于日后扩展和变化。

不管是进程还是线程,每次阻塞、切换都需要陷入系统调用(system call),先让CPU跑操作系统的调度程序,然后再由调度程序决定该跑哪一个进程(线程)。多个线程之间在一些访问互斥的代码时还需要加上锁,

现下流行的异步server都是基于事件驱动的(如nginx)。

异步事件驱动模型中,把会导致阻塞的操作转化为一个异步操作,主线程负责发起这个异步操作,并处理这个异步操作的结果。由于所有阻塞的操作都转化为异步操作,理论上主线程的大部分时间都是在处理实际的计算任务,少了多线程的调度时间,所以这种模型的性能通常会比较好。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
浅析Python的Django框架中的Memcached
Jul 23 Python
学习python 之编写简单乘法运算题
Feb 27 Python
Python常见格式化字符串方法小结【百分号与format方法】
Sep 18 Python
Python中pow()和math.pow()函数用法示例
Feb 11 Python
python实现将读入的多维list转为一维list的方法
Jun 28 Python
numpy数组之存取文件的实现示例
May 24 Python
python3 tcp的粘包现象和解决办法解析
Dec 09 Python
python读取ini配置的类封装代码实例
Jan 08 Python
python爬虫开发之PyQuery模块详细使用方法与实例全解
Mar 09 Python
Python 字节流,字符串,十六进制相互转换实例(binascii,bytes)
May 11 Python
教你利用Selenium+python自动化来解决pip使用异常
May 20 Python
Python数据类型最全知识总结
May 31 Python
微信 用脚本查看是否被微信好友删除
Oct 28 #Python
使用Python将数组的元素导出到变量中(unpacking)
Oct 27 #Python
Python中断言Assertion的一些改进方案
Oct 27 #Python
利用Python实现颜色色值转换的小工具
Oct 27 #Python
Python实现批量检测HTTP服务的状态
Oct 27 #Python
python解决网站的反爬虫策略总结
Oct 26 #Python
Python控制多进程与多线程并发数总结
Oct 26 #Python
You might like
php图片验证码代码
2008/03/27 PHP
php银联网页支付实现方法
2015/03/04 PHP
基于PHP实现假装商品限时抢购繁忙的效果
2015/10/16 PHP
php rsa 加密,解密,签名,验签详解
2016/12/06 PHP
PHP下 Mongodb 连接远程数据库的实例代码
2017/08/30 PHP
PHP保存Base64图片base64_decode的问题整理
2019/11/04 PHP
PHP实现创建一个RPC服务操作示例
2020/02/23 PHP
Jquery ajaxsubmit上传图片实现代码
2010/11/04 Javascript
利用谷歌地图API获取点与点的距离的js代码
2012/10/11 Javascript
使用JS或jQuery模拟鼠标点击a标签事件代码
2014/03/10 Javascript
JavaScript调试技巧之console.log()详解
2014/03/19 Javascript
get(0).tagName获得作用标签示例代码
2014/10/08 Javascript
《JavaScript DOM 编程艺术》读书笔记之JavaScript 简史
2015/01/09 Javascript
JavaScript中property和attribute的区别详细介绍
2015/03/03 Javascript
jQuery手指滑动轮播效果
2016/12/22 Javascript
Vue.js实现表格动态增加删除的方法(附源码下载)
2017/01/20 Javascript
解析jquery easyui tree异步加载子节点问题
2017/03/08 Javascript
Bootstrap下拉菜单Dropdowns的实现代码
2017/03/17 Javascript
VUE使用vuex解决模块间传值问题的方法
2017/06/01 Javascript
JS实现自定义弹窗功能
2018/08/08 Javascript
Vue 中的受控与非受控组件的实现
2018/12/17 Javascript
微信小程序学习笔记之登录API与获取用户信息操作图文详解
2019/03/29 Javascript
ES6 Proxy实现Vue的变化检测问题
2019/06/11 Javascript
在Python中通过threading模块定义和调用线程的方法
2016/07/12 Python
python判断链表是否有环的实例代码
2020/01/31 Python
Python脚本去除文件的只读性操作
2020/03/05 Python
英国复古服装购物网站:Collectif
2019/10/30 全球购物
工程专业毕业生自荐信范文
2013/12/25 职场文书
贺卡寄语大全
2014/04/11 职场文书
文明社区申报材料
2014/08/21 职场文书
2014学习优秀共产党员先进事迹材料思想汇报
2014/09/14 职场文书
出售房屋委托书范本
2014/09/24 职场文书
个人工作作风整改措施思想汇报
2014/10/13 职场文书
2015年党风建设工作总结
2015/04/29 职场文书
导游词之神仙居景区
2019/11/15 职场文书
如何在CSS中绘制曲线图形及展示动画
2021/05/24 HTML / CSS