Python实现进程同步和通信的方法


Posted in Python onJanuary 02, 2018

Python中的多线程其实并不是真正的多线程,如果想要充分地使用多核CPU的资源,在python中大部分情况需要使用多进程。Python提供了非常好用的多进程包multiprocessing,只需要定义一个函数,Python会完成其他所有事情。借助这个包,可以轻松完成从单进程到并发执行的转换。multiprocessing支持子进程、通信和共享数据、执行不同形式的同步,提供了Process、Queue、Pipe、Lock等组件。

引例:

如之前创建多进程的例子

# -*- coding:utf-8 -*-
from multiprocessing import Process,Pool
import os,time

def run_proc(name):    ##定义一个函数用于进程调用
  for i in range(5):  
    time.sleep(0.2)  #休眠0.2秒
    print 'Run child process %s (%s)' % (name, os.getpid())
#执行一次该函数共需1秒的时间

if __name__ =='__main__': #执行主进程
  print 'Run the main process (%s).' % (os.getpid())
  mainStart = time.time() #记录主进程开始的时间
  p = Pool(8)      #开辟进程池
  for i in range(16):                 #开辟14个进程
    p.apply_async(run_proc,args=('Process'+str(i),))#每个进程都调用run_proc函数,
                            #args表示给该函数传递的参数。

  print 'Waiting for all subprocesses done ...'
  p.close() #关闭进程池
  p.join() #等待开辟的所有进程执行完后,主进程才继续往下执行
  print 'All subprocesses done'
  mainEnd = time.time() #记录主进程结束时间
  print 'All process ran %0.2f seconds.' % (mainEnd-mainStart) #主进程执行时间

运行结果:

Run the main process (36652). 
Waiting for all subprocesses done … 
Run child process Process0 (36708)Run child process Process1 (36748)

Run child process Process3 (36736) 
Run child process Process2 (36716) 
Run child process Process4 (36768)

如第3行的输出,偶尔会出现这样不如意的输入格式,为什么呢?

原因是多个进程争用打印输出资源的结果。前一个进程为来得急输出换行符,该资源就切换给了另一个进程使用,致使两个进程输出在同一行上,而前一个进程的换行符在下一次获得资源时才打印输出。

Lock

为了避免这种情况,需在进程进入临界区(使进程进入临界资源的那段代码,称为临界区)时加锁。
可以向如下这样添加锁后看看执行效果:

# -*- coding:utf-8 -*-

lock = Lock()  #申明一个全局的lock对象
def run_proc(name):
  global lock   #引用全局锁
  for i in range(5):
    time.sleep(0.2)
    lock.acquire() #申请锁
    print 'Run child process %s (%s)' % (name, os.getpid())
    lock.release()  #释放锁

Semaphore

Semaphore为信号量机制。当共享的资源拥有多个时,可用Semaphore来实现进程同步。其用法和Lock差不多,s = Semaphore(N),每执行一次s.acquire(),该资源的可用个数将减少1,当资源个数已为0时,就进入阻塞;每执行一次s.release(),占用的资源被释放,该资源的可用个数增加1。

多进程的通信(信息交互)

不同进程之间进行数据交互,可能不少刚开始接触多进程的同学会想到共享全局变量的方式,这样通过向全局变量写入和读取信息便能实现信息交互。但是很遗憾,并不能这样实现。

下面通过例子,加深对那篇文章的理解:

# -*- coding:utf-8 -*-
from multiprocessing import Process, Pool
import os
import time
L1 = [1, 2, 3]
def add(a, b):
  global L1
  L1 += range(a, b)
  print L1
if __name__ == '__main__':
  p1 = Process(target=add, args=(20, 30))
  p2 = Process(target=add, args=(30, 40))
  p1.start()
  p2.start()
  p1.join()
  p2.join()
  print L1

输出结果:

[1, 2, 3, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
[1, 2, 3, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39]
[1, 2, 3]

该程序的原本目的是想将两个子进程生成的列表加到全局变量L1中,但用该方法并不能达到想要的效果。既然不能通过全局变量来实现不同进程间的信息交互,那有什么办法呢。

mutiprocessing为我们可以通过Queue和Pipe来实现进程间的通信。

Queue

按上面的例子通过Queue来实现:

# -*- coding:utf-8 -*-
from multiprocessing import Process, Queue, Lock
L = [1, 2, 3]
def add(q, lock, a, b):
  lock.acquire() # 加锁避免写入时出现不可预知的错误
  L1 = range(a, b)
  lock.release()
  q.put(L1)
  print L1
if __name__ == '__main__':
  q = Queue()
  lock = Lock()
  p1 = Process(target=add, args=(q, lock, 20, 30))
  p2 = Process(target=add, args=(q, lock, 30, 40))
  p1.start()
  p2.start()
  p1.join()
  p2.join()
  L += q.get() + q.get()
  print L

 执行结果:

[20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
[30, 31, 32, 33, 34, 35, 36, 37, 38, 39]
[1, 2, 3, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39]

下面介绍Queue的常用方法:

  1. 定义时可用q = Queue(maxsize = 10)来指定队列的长度,默认时或maxsize值小于1时队列为无限长度。
  2. q.put(item)方法向队列放入元素,其还有一个可选参数block,默认为True,此时若队列已满则会阻塞等待,直到有空闲位置。而当black值为 False,在该情况下就会抛出Full异 常
  3. Queue是不可迭代的对象,不能通过for循环取值,取值时每次调用q.get()方法。同样也有可选参数block,默认为True,若此时队列为空则会阻塞等待。而black值为False时,在该情况下就会抛出Empty异常
  4. Queue.qsize() 返回队列的大小
  5. Queue.empty() 如果队列为空,返回True,反之False
  6. Queue.full() 如果队列满了,返回True,反之False
  7. Queue.get([block[, timeout]]) 获取队列,timeout等待时间Queue.get_nowait() 相当Queue.get(False) 非阻塞 Queue.put(item) 写入队列,timeout等待时间
  8. Queue.put_nowait(item) 相当Queue.put(item, False)

Pipe

Pipe管道,可以是单向(half-duplex),也可以是双向(duplex)。我们通过mutiprocessing.Pipe(duplex=False)创建单向管道 (默认为双向)。双向Pipe允许两端的进即可以发送又可以接受;单向的Pipe只允许前面的端口用于接收,后面的端口用于发送。

下面给出例子:

# -*- coding:utf-8 -*-
from multiprocessing import Process, Pipe
def proc1(pipe):
  s = 'Hello,This is proc1'
  pipe.send(s)
def proc2(pipe):
  while True:
    print "proc2 recieve:", pipe.recv()
if __name__ == "__main__":
  pipe = Pipe()
  p1 = Process(target=proc1, args=(pipe[0],))
  p2 = Process(target=proc2, args=(pipe[1],))
  p1.start()
  p2.start()
  p1.join()
  p2.join(2)  #限制执行时间最多为2秒
  print '\nend all processes.'

执行结果如下:

proc2 recieve: Hello,This is proc1
proc2 recieve:
end all processes.

当第二行输出后,因为管道中没有数据传来,Proc2处于阻塞状态,2秒后被强制结束。

以下是单向管道的例子,注意pipe[0],pipe[1]的分配。

# -*- coding:utf-8 -*-
from multiprocessing import Process, Pipe
def proc1(pipe):
  s = 'Hello,This is proc1'
  pipe.send(s)
def proc2(pipe):
  while True:
    print "proc2 recieve:", pipe.recv()
if __name__ == "__main__":
  pipe = Pipe(duplex=False)
  p1 = Process(target=proc1, args=(pipe[1],)) #pipe[1]为发送端
  p2 = Process(target=proc2, args=(pipe[0],)) #pipe[0]为接收端
  p1.start()
  p2.start()
  p1.join()
  p2.join(2) # 限制执行时间最多为2秒
  print '\nend all processes.'

执行结果同上。

强大的Manage

Queue和Pipe实现的数据共享方式只支持两种结构 Value 和 Array。Python中提供了强大的Manage专门用来做数据共享,其支持的类型非常多,包括: Value,Array,list, dict,Queue, Namespace, Lock, RLock, Semaphore, BoundedSemaphore, Condition, Event等

其用法如下:

from multiprocessing import Process, Manager
def func(dt, lt):
  for i in range(10):
    key = 'arg' + str(i)
    dt[key] = i * i

  lt += range(11, 16)

if __name__ == "__main__":
  manager = Manager()
  dt = manager.dict()
  lt = manager.list()

  p = Process(target=func, args=(dt, lt))
  p.start()
  p.join()
  print dt, '\n', lt

执行结果:

{‘arg8': 64, ‘arg9': 81, ‘arg0': 0, ‘arg1': 1, ‘arg2': 4, ‘arg3': 9, ‘arg4': 16, ‘arg5': 25, ‘arg6': 36, ‘arg7': 49}
[11, 12, 13, 14, 15]

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

Python 相关文章推荐
初学python数组的处理代码
Jan 04 Python
python实现图片批量剪切示例
Mar 25 Python
python中in在list和dict中查找效率的对比分析
May 04 Python
Windows下python3.7安装教程
Jul 31 Python
python 获取url中的参数列表实例
Dec 18 Python
Python使用pandas和xlsxwriter读写xlsx文件的方法示例
Apr 09 Python
django一对多模型以及如何在前端实现详解
Jul 24 Python
详解numpy.meshgrid()方法使用
Aug 01 Python
Python绘制热力图示例
Sep 27 Python
利用python Selenium实现自动登陆京东签到领金币功能
Oct 31 Python
Python3.x+pyqtgraph实现数据可视化教程
Mar 14 Python
python爬虫中采集中遇到的问题整理
Nov 27 Python
mac系统安装Python3初体验
Jan 02 #Python
Python中static相关知识小结
Jan 02 #Python
python tensorflow基于cnn实现手写数字识别
Jan 01 #Python
python+selenium实现163邮箱自动登陆的方法
Dec 31 #Python
python 类对象和实例对象动态添加方法(分享)
Dec 31 #Python
利用python将图片转换成excel文档格式
Dec 30 #Python
书单|人生苦短,你还不用python!
Dec 29 #Python
You might like
咖啡与牛奶
2021/03/03 冲泡冲煮
PHP批量生成缩略图的代码
2008/07/19 PHP
PHP统计目录下的文件总数及代码行数(去除注释及空行)
2011/01/17 PHP
PHP实现模拟http请求的方法分析
2017/12/20 PHP
基于jQuery图片平滑连续滚动插件
2009/04/27 Javascript
jquery Firefox3.5中操作select的问题
2009/07/10 Javascript
JavaScript简单判断复选框是否选中及取出值的方法
2015/08/13 Javascript
jQuery ui实现动感的圆角渐变网站导航菜单效果代码
2015/08/26 Javascript
jQuery表单验证功能实例
2015/08/28 Javascript
Nodejs学习item【入门手上】
2016/05/05 NodeJs
jquery把int类型转换成字符串类型的方法
2016/10/07 Javascript
Angular4 中内置指令的基本用法
2017/07/31 Javascript
vue axios 二次封装的示例代码
2017/12/08 Javascript
vue计算属性和监听器实例解析
2018/05/10 Javascript
Vuex新手的理解与使用详解
2019/05/31 Javascript
解决 window.onload 被覆盖的问题方法
2020/01/14 Javascript
Vue3 的响应式和以前有什么区别,Proxy 无敌?
2020/05/20 Javascript
js事件机制----捕获与冒泡机制实例分析
2020/05/22 Javascript
echarts实现晶体球面投影的实例教程
2020/10/10 Javascript
Vue自定义表单内容检查rules实例
2020/10/30 Javascript
python2.7 json 转换日期的处理的示例
2018/03/07 Python
基于python的ini配置文件操作工具类
2019/04/24 Python
SELENIUM自动化模拟键盘快捷键操作实现解析
2019/10/28 Python
用python发送微信消息
2020/12/21 Python
寻找完美的房车租赁:RVShare
2019/02/23 全球购物
第二层交换机和路由器的区别?第三层交换机和路由器的区别?
2013/05/23 面试题
通息工程毕业生自荐信
2013/10/16 职场文书
入股协议书
2014/04/14 职场文书
护士找工作求职信
2014/07/02 职场文书
2014年银行客户经理工作总结
2014/11/12 职场文书
2015新员工试用期工作总结
2014/12/12 职场文书
工程部文员岗位职责
2015/02/04 职场文书
JavaScript嵌入百度地图API的最详细方法
2021/04/16 Javascript
Go语言带缓冲的通道实现
2021/04/26 Golang
国庆节到了,利用JS实现一个生成国庆风头像的小工具 详解实现过程
2021/10/05 Javascript
MySQL分区路径子分区再分区
2022/04/13 MySQL