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抓取网页内容示例分享
Feb 24 Python
python实现的一个火车票转让信息采集器
Jul 09 Python
python中Apriori算法实现讲解
Dec 10 Python
python 自定义异常和异常捕捉的方法
Oct 18 Python
Python运行不显示DOS窗口的解决方法
Oct 22 Python
python selenium 弹出框处理的实现
Feb 26 Python
python对验证码降噪的实现示例代码
Nov 12 Python
Python数据分析pandas模块用法实例详解
Nov 20 Python
keras模型可视化,层可视化及kernel可视化实例
Jan 24 Python
python pprint模块中print()和pprint()两者的区别
Feb 10 Python
Python基于requests库爬取网站信息
Mar 02 Python
Python建造者模式案例运行原理解析
Jun 29 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
php下HTTP Response中的Chunked编码实现方法
2008/11/19 PHP
thinkphp模板输出技巧汇总
2014/11/24 PHP
页面利用渐进式JPEG来提升用户体验度
2014/12/01 PHP
PHP explode()函数的几个应用和implode()函数有什么区别
2015/11/05 PHP
Thinkphp3.2简单解决多文件上传只上传一张的问题
2017/09/26 PHP
php爬取天猫和淘宝商品数据
2018/02/23 PHP
Div Select挡住的解决办法
2008/08/07 Javascript
window.onbeforeunload方法在IE下无法正常工作的解决办法
2010/01/23 Javascript
js简单的表格添加行和删除行操作示例
2014/03/31 Javascript
jQuery控制的不同方向的滑动(向左、向右滑动等)
2014/07/18 Javascript
JavaScript 事件入门知识
2015/04/13 Javascript
jQuery实现自动调整字体大小的方法
2015/06/15 Javascript
JS实现的通用表单验证插件完整实例
2015/08/20 Javascript
NodeJS与HTML5相结合实现拖拽多个文件上传到服务器的实现方法
2016/07/26 NodeJs
Angular2 (RC5) 路由与导航详解
2016/09/21 Javascript
Vue.js 实现数据展示全部和收起功能
2018/09/05 Javascript
详解微信小程序-获取用户session_key,openid,unionid - 后端为nodejs
2019/04/29 NodeJs
配置node服务器并且链接微信公众号接口配置步骤详解
2019/06/21 Javascript
JavaScript表格隔行变色和Tab标签页特效示例【附jQuery版】
2019/07/11 jQuery
js实现简单进度条效果
2020/03/25 Javascript
python连接mysql调用存储过程示例
2014/03/05 Python
深入浅析Python中join 和 split详解(推荐)
2016/06/30 Python
使用Python的Flask框架表单插件Flask-WTF实现Web登录验证
2016/07/12 Python
TensorFlow实现指数衰减学习率的方法
2020/02/05 Python
解决Python中报错TypeError: must be str, not bytes问题
2020/04/07 Python
python将数据插入数据库的代码分享
2020/08/16 Python
美国定制钻石订婚戒指:Ritani
2017/12/08 全球购物
英国领先的新鲜松露和最好的松露产品供应商:TruffleHunter
2019/08/26 全球购物
给定一个时间点,希望得到其他时间点
2013/11/07 面试题
医学院学生的自我评价分享
2013/11/19 职场文书
中国梦的演讲稿
2014/01/08 职场文书
申报材料格式
2014/12/30 职场文书
法院个人总结
2015/03/03 职场文书
2015年文员个人工作总结
2015/04/09 职场文书
土木工程毕业答辩开场白
2015/05/29 职场文书
超级实用!五步法则,教你写好年终工作总结
2019/12/05 职场文书