Python并发之多进程的方法实例代码


Posted in Python onAugust 15, 2018

一,进程的理论基础

一个应用程序,归根结底是一堆代码,是静态的,而进程才是执行中的程序,在一个程序运行的时候会有多个进程并发执行。

进程和线程的区别:

  • 进程是系统资源分配的基本单位。
  • 一个进程内可以包含多个线程,属于一对多的关系,进程内的资源,被其内的线程共享
  • 线程是进程运行的最小单位,如果说进程是完成一个功能,那么其线程就是完成这个功能的基本单位
  • 进程间资源不共享,多进程切换资源开销,难度大,同一进程内的线程资源共享,多线程切换资源开销,难度小

进程与线程的共同点:

都是为了提高程序运行效率,都有执行的优先权

二,Python的多进程( multiprocessing模块)

创建一个进程(和创建线程类似)

方法一:创建Process对象,通过对象调用start()方法启动进程

from multiprocessing import Process
def foo(name):
 print('hello,%s'%name)
if __name__ == '__main__':
 p1=Process(target=foo,args=('world',))
 p2 = Process(target=foo, args=('China',))
 p1.start()
 p2.start()
 print('=====主进程=====')
 # == == =主进程 == == =
 # hello, world
 # hello, China
 #主进程和子进程并发执行

注意:Process对象只能在在 if __name__ == '__main__':下创建,不然会报错。

方法二:自定义一个类继承Process类,并重写run()方法,将执行代码放在其内

from multiprocessing import Process
class MyProcess(Process):
 def __init__(self,name):
  super().__init__()
  self.name = name
 def run(self):
  print('hello,%s'%self.name)
if __name__ == '__main__':
 myprocess1 = MyProcess('world')
 myprocess2 = MyProcess('world')
 myprocess1.start()
 myprocess2.start()

Process内置方法

实例方法:

p.start():启动进程,并调用该子进程中的p.run()

p.run():进程启动时运行的方法,正是它去调用target指定的函数,我们自定义类的类中一定要实现该方法 

p.terminate():强制终止进程p,不会进行任何清理操作,如果p创建了子进程,该子进程就成了僵尸进程,使用该方法需要特别小心这种情况。如果p还保存了一个锁那么也将不会被释放,进而导致死锁

p.is_alive():如果p仍然运行,返回True

p.join([timeout]):主线程等待p终止。timeout是可选的超时时间
Process属性

p.daemon:默认值为False,如果设为True,代表p为后台运行的守护进程,当p的父进程终止时,p也随之终止,并且设定为True后,p不能创建自己的新进程,必须在p.start()之前设置

p.name:进程的名称

p.pid:进程的pid

p.exitcode:进程在运行时为None、如果为?N,表示被信号N结束(了解即可)

守护进程

类似于守护线程,只不过守护线程是对象的一个方法,而守护进程封装成对象的属性。

from multiprocessing import Process
import time
class MyProcess(Process):
 def __init__(self,name):
  super().__init__()
  self.name = name
 def run(self):
  time.sleep(3)
  print('hello,%s'%self.name)
if __name__ == '__main__':
 myprocess1=MyProcess('world')
 myprocess1.daemon = True
 myprocess1.start()
 print('结束')
#不会输出‘hello world',因为设置为守护进程,主进程不会等待

也可以使用join方法,使主进程等待

from multiprocessing import Process
import time
class MyProcess(Process):
 def __init__(self,name):
  super().__init__()
  self.name = name
 def run(self):
  time.sleep(3)
  print('hello,%s'%self.name)
if __name__ == '__main__':
 myprocess1=MyProcess('world')
 myprocess1.daemon = True
 myprocess1.start()
 myprocess1.join() #程序阻塞
 print('结束')
join()

进程同步和锁

进程虽然不像线程共享资源,但是这并不意味着进程间不 需要加锁,比如不同进程会共享同一个终端 ( 屏幕),或者操作同一个文件,数据库,那么数据安全还是很有必要的,因此我们可以加锁,

from multiprocessing import Process,Lock
import time
def a_print(l): #需要传入对象,因为信息不共享
 l.acquire()
 print('我要打印信息')
 time.sleep(1)
 print('我打印完了')
 l.release()
if __name__ == '__main__':
 l = Lock()
 for i in range(20):
  p = Process(target=a_print,args=(l,))
  p.start()

信号量(Semaphore)

能够并发执行的进程数,超出的进程阻塞,直到有进程运行完成。

Semaphore管理一个内置的计数器,

每当调用acquire()时内置计数器-1;

调用release() 时内置计数器+1;

计数器不能小于0;当计数器为0时,acquire()将阻塞进程直到其他进程调用release()。

from multiprocessing import Process,Queue,Semaphore
import time,random
def seat(s,n):
 s.acquire()
 print('学生%d坐下了'%n)
 time.sleep(random.randint(1,2))
 s.release()
if __name__ == '__main__':
 s = Semaphore(5)
 for i in range(20):
  p = Process(target=seat,args=(s,i))
  p.start()
 print('-----主进程-------')

注意:其实信号量和锁类似,只是限制进程运行某个代码块的数量(锁为1个),并不是能限制并发的进程,如上述代码,一次性还是创建了20个进程

事件(Event)

from multiprocessing import Process,Event
import time, random
def eating(event):
 event.wait()
 print('去吃饭的路上...')
def makeing(event):
 print('做饭中')
 time.sleep(random.randint(1,2))
 print('做好了,快来...')
 event.set()
if __name__ == '__main__':
 event=Event()
 t1 = Process(target=eating,args=(event,))
 t2 = Process(target=makeing,args=(event,))
 t1.start()
 t2.start()
 # 做饭中
 # 做好了,快来...
 # 去吃饭的路上...

和线程事件几乎一致

进程队列(Queue)

进程队列是进程通讯的方式之一。使用multiprocessing 下的Queue

from multiprocessing import Process,Queue
import time
def func1(queue):
 while True:
  info=queue.get()
  if info == None:
   return 
  print(info)
def func2(queue):
 for i in range(10):
  time.sleep(1)
  queue.put('is %d'%i)
 queue.put(None) #结束的标志
if __name__ == '__main__':
 q = Queue()
 p1 = Process(target=func1,args=(q,))
 p2 = Process(target=func2, args=(q,))
 p1.start()
 p2.start()
Queue类的方法,源码如下:
class Queue(object):
 def __init__(self, maxsize=-1): #可以传参设置队列最大容量
  self._maxsize = maxsize
 def qsize(self): #返回当前时刻队列中的个数
  return 0
 def empty(self): #是否为空
  return False
 def full(self): 是否满了
  return False
 def put(self, obj, block=True, timeout=None): #放值,blocked和timeout。如果blocked为True(默认值),并且timeout为正值,该方法会阻塞timeout指定的时间,直到该队列有剩余的空间。如果超时,会抛出Queue.Full异常。如果blocked为False,但该Queue已满,会立即抛出Queue.Full异常
  pass
 def put_nowait(self, obj): #=put(False)
  pass
 def get(self, block=True, timeout=None): 获取值,get方法有两个可选参数:blocked和timeout。如果blocked为True(默认值),并且timeout为正值,那么在等待时间内没有取到任何元素,会抛出Queue.Empty异常。如果blocked为False,有两种情况存在,如果Queue有一个值可用,则立即返回该值,否则,如果队列为空,则立即抛出Queue.Empty异常.
  pass
 def get_nowait(self): # = get(False)
  pass
 def close(self): #将队列关闭
  pass
 def join_thread(self): #略,几乎不用
  pass
 def cancel_join_thread(self):
  pass

进程队列源码注释

进程池

进程的消耗是很大的,因此我们不能无节制的开启新进程,因此我们可以 通过维护一个进程池来控制进程的数量 。这就不同于信号量,进程池可以从源头控制进程数量。在Python中可以通过如下方法使用

同步调用

from multiprocessing import Pool
import time, random, os
def func(n):
 pid = os.getpid()
 print('进程%s正在处理第%d个任务'%(pid,n),'时间%s'%time.strftime('%H-%M-%S'))
 time.sleep(2)
 res = '处理%s'%random.choice(['成功','失败'])
 return res
if __name__ == '__main__':
 p = Pool(4) #创建4个进程,
 li = []
 for i in range(10):
  res = p.apply(func,args=(i,)) 交给进程池处理,处理完成才返回值,会阻塞,即使池内还有空余进程,相当于顺序执行
  li.append(res)
 for i in li:
  print(i)

#进程1916正在处理第0个任务 时间21-02-53
#进程1240正在处理第1个任务 时间21-02-55
#进程3484正在处理第2个任务 时间21-02-57
#进程7512正在处理第3个任务 时间21-02-59
#进程1916正在处理第4个任务 时间21-03-01
#进程1240正在处理第5个任务 时间21-03-03
#进程3484正在处理第6个任务 时间21-03-05
#进程7512正在处理第7个任务 时间21-03-07
#进程1916正在处理第8个任务 时间21-03-09
#进程1240正在处理第9个任务 时间21-03-11

从结果可以发现两点:

  1. 不是并发处理
  2. 一直都只有四个进程,串行执行

因此进程池提供了 异步处理 的方式

from multiprocessing import Pool
import time, random, os
def func(n):
 pid = os.getpid()
 print('进程%s正在处理第%d个任务'%(pid,n),'时间%s'%time.strftime('%H-%M-%S'))
 time.sleep(2)
 res = '处理%s'%random.choice(['成功','失败'])
 return res

if __name__ == '__main__':
 p = Pool(4)
 li = []
 for i in range(10):
  res = p.apply_async(func,args=(i,)) 结果不会立刻返回,遇到阻塞,开启下一个进程,在这,相当于几乎同时出现四个打印结果(一个线程处理一个任务,处理完下个任务才能进来)
  li.append(res)

 p.close() #join之前需要关闭进程池
 p.join() #因为异步,所以需要等待池内进程工作结束再继续
 for i in li:
  print(i.get()) #i是一个对象,通过get方法获取返回值,而同步则没有该方法

关于回调函数

from multiprocessing import Pool
import time, random, os
def func(n):
 pid = os.getpid()
 print('进程%s正在处理第%d个任务'%(pid,n),'时间%s'%time.strftime('%H-%M-%S'))
 time.sleep(2)
 res = '处理%s'%random.choice(['成功','失败'])
 return res

def foo(info):
 print(info) #传入值为进程执行结果

if __name__ == '__main__':
 p = Pool(4)
 li = []
 for i in range(10):
  res = p.apply_async(func,args=(i,),callback = foo) callback()回调函数会在进程执行完之后调用(主进程调用) 
  li.append(res)

 p.close() 
 p.join() 
 for i in li:
  print(i.get())

有回调函数

总结

以上所述是小编给大家介绍的Python并发之多进程的方法实例代码,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Python 相关文章推荐
如何解决django配置settings时遇到Could not import settings 'conf.local'
Nov 18 Python
python实现中文分词FMM算法实例
Jul 10 Python
Django中对通过测试的用户进行限制访问的方法
Jul 23 Python
python搭建虚拟环境的步骤详解
Sep 27 Python
Python列表list解析操作示例【整数操作、字符操作、矩阵操作】
Jul 25 Python
Python元字符的用法实例解析
Jan 17 Python
详解Pytorch 使用Pytorch拟合多项式(多项式回归)
May 24 Python
Python 忽略warning的输出方法
Oct 18 Python
简单了解python高阶函数map/reduce
Jun 28 Python
Python 点击指定位置验证码破解的实现代码
Sep 11 Python
如何利用Python识别图片中的文字
May 31 Python
Python实现一个简单的毕业生信息管理系统的示例代码
Jun 08 Python
Python使用sort和class实现的多级排序功能示例
Aug 15 #Python
Python常见排序操作示例【字典、列表、指定元素等】
Aug 15 #Python
Centos下实现安装Python3.6和Python2共存
Aug 15 #Python
详解Python中的type和object
Aug 15 #Python
python 通过 socket 发送文件的实例代码
Aug 14 #Python
Python 打印中文字符的三种方法
Aug 14 #Python
django如何连接已存在数据的数据库
Aug 14 #Python
You might like
Linux下 php5 MySQL5 Apache2 phpMyAdmin ZendOptimizer安装与配置[图文]
2008/11/18 PHP
SAE实时日志接口SDK用法示例
2016/10/09 PHP
PHP+ajax实现二级联动菜单功能示例
2018/08/10 PHP
Nginx+php配置文件及原理解析
2020/12/09 PHP
js调用flash的效果代码
2008/04/26 Javascript
一个简单的JavaScript数据缓存系统实现代码
2010/10/24 Javascript
js中parseFloat(参数1,参数2)定义和用法及注意事项
2013/01/27 Javascript
三种方式获取XMLHttpRequest对象
2014/04/21 Javascript
JavaScript动态创建link标签到head里的方法
2014/12/22 Javascript
JavaScript中的逻辑判断符&&、||与!介绍
2014/12/31 Javascript
详解AngularJS中的filter过滤器用法
2016/01/04 Javascript
JS递归遍历对象获得Value值方法技巧
2016/06/14 Javascript
Nodejs抓取html页面内容(推荐)
2016/08/11 NodeJs
微信小程序 教程之事件
2016/10/18 Javascript
AngularJS ng-template寄宿方式用法分析
2016/11/07 Javascript
vue.js声明式渲染和条件与循环基础知识
2017/07/31 Javascript
JS实现的抛物线运动效果示例
2018/01/30 Javascript
JS实现运动缓冲效果的封装函数示例
2018/02/18 Javascript
NodeJs之word文件生成与解析的实现代码
2019/04/01 NodeJs
jQuery实现动态加载(按需加载)javascript文件的方法分析
2019/05/31 jQuery
详解将微信小程序接口Promise化并使用async函数
2019/08/05 Javascript
快速解决vue2+vue-cli3项目ie兼容的问题
2020/11/17 Vue.js
[45:34]完美世界DOTA2联赛PWL S3 Rebirth vs CPG 第一场 12.18
2020/12/19 DOTA
python中解析json格式文件的方法示例
2017/05/03 Python
Python语言描述连续子数组的最大和
2018/01/04 Python
基于Python实现签到脚本过程解析
2019/10/25 Python
django使用xadmin的全局配置详解
2019/11/15 Python
pycharm无法导入本地模块的解决方式
2020/02/12 Python
python集成开发环境配置(pycharm)
2020/02/14 Python
单位领导证婚词
2014/01/14 职场文书
高一生物教学反思
2014/01/17 职场文书
售后服务经理岗位职责范本
2014/02/22 职场文书
创建青年文明号材料
2014/05/09 职场文书
写给医院的感谢信
2015/01/22 职场文书
刘胡兰观后感
2015/06/16 职场文书
vue3使用vuedraggable实现拖拽功能
2022/04/06 Vue.js