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 time模块详解(常用函数实例讲解,非常好)
Apr 24 Python
python海龟绘图实例教程
Jul 24 Python
Python3 串口接收与发送16进制数据包的实例
Jun 12 Python
Pandas DataFrame数据的更改、插入新增的列和行的方法
Jun 25 Python
python实现跨excel sheet复制代码实例
Mar 03 Python
python小白学习包管理器pip安装
Jun 09 Python
keras自定义损失函数并且模型加载的写法介绍
Jun 15 Python
通过实例解析Python RPC实现原理及方法
Jul 07 Python
Python matplotlib模块及柱状图用法解析
Aug 10 Python
一行代码python实现文件共享服务器
Apr 22 Python
python本地文件服务器实例教程
May 02 Python
一文搞懂Python Sklearn库使用
Aug 23 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
WINDOWS服务器安装多套PHP的另类解决方案
2006/10/09 PHP
ThinkPHP令牌验证实例
2014/06/18 PHP
php、mysql查询当天,查询本周,查询本月的数据实例(字段是时间戳)
2017/02/04 PHP
LAMP环境使用Composer安装Laravel的方法
2017/03/25 PHP
Windows 下安装 swoole 图文教程(php)
2017/06/05 PHP
Javascript Function对象扩展之延时执行函数
2010/07/06 Javascript
jquery数组封装使用方法分享(jquery数组遍历)
2014/03/25 Javascript
jquery获取当前日期的方法
2015/01/14 Javascript
thinkphp 表名 大小写 窍门
2015/02/01 Javascript
在JS中a标签加入单击事件屏蔽href跳转页面
2016/12/16 Javascript
jQuery实现手机上输入后隐藏键盘功能
2017/01/04 Javascript
关于jQuery EasyUI 中刷新Tab选项卡后一个页面变形的解决方法
2017/03/02 Javascript
js实现单张图片平移切换效果
2017/10/11 Javascript
vue在自定义组件中使用v-model进行数据绑定的方法
2019/03/25 Javascript
VUE前后端学习tab写法实例
2019/08/06 Javascript
javascript设计模式 ? 简单工厂模式原理与应用实例分析
2020/04/09 Javascript
Python实现的概率分布运算操作示例
2017/08/14 Python
Python解压 rar、zip、tar文件的方法
2019/11/19 Python
Python自动化测试笔试面试题精选
2020/03/12 Python
Python3+selenium实现cookie免密登录的示例代码
2020/03/18 Python
python爬虫实现爬取同一个网站的多页数据的实例讲解
2021/01/18 Python
详解CSS3 filter:drop-shadow滤镜与box-shadow区别与应用
2020/08/24 HTML / CSS
捷克时尚网上商店:OTTO
2018/03/15 全球购物
印度最好的在线药品订购网站:PharmEasy
2018/11/30 全球购物
北京华建集团SQL面试题
2014/06/03 面试题
自荐信格式技巧有哪些呢
2013/11/19 职场文书
如何写你的创业计划书
2014/01/07 职场文书
新教师工作感言
2014/02/16 职场文书
保险专业自荐信范文
2014/02/20 职场文书
寒假安全保证书
2015/02/28 职场文书
2015年医院药剂科工作总结
2015/05/04 职场文书
干货:如何写好工作总结报告!
2019/05/10 职场文书
Python办公自动化解决world文件批量转换
2021/09/15 Python
Oracle表空间与权限的深入讲解
2021/11/17 Oracle
Vue+Flask实现图片传输功能
2022/04/01 Vue.js
vue打包时去掉所有的console.log
2022/04/10 Vue.js