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 相关文章推荐
Python中声明只包含一个元素的元组数据方法
Aug 25 Python
简单理解Python中的装饰器
Jul 31 Python
举例讲解Python设计模式编程的代理模式与抽象工厂模式
Jan 16 Python
django celery redis使用具体实践
Apr 08 Python
基于sklearn实现Bagging算法(python)
Jul 11 Python
python 通过手机号识别出对应的微信性别(实例代码)
Dec 22 Python
解决TensorFlow GPU版出现OOM错误的问题
Feb 03 Python
配置python的编程环境之Anaconda + VSCode的教程
Mar 29 Python
如何使用python切换hosts文件
Apr 29 Python
matplotlib基础绘图命令之imshow的使用
Aug 13 Python
jupyter 添加不同内核的操作
Feb 06 Python
python 单机五子棋对战游戏
Apr 28 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
PHP限制页面只能在微信自带浏览器访问的代码
2014/01/15 PHP
php中session使用示例
2014/03/29 PHP
基础的WordPress插件制作教程
2015/11/24 PHP
编写PHP脚本清除WordPress头部冗余代码的方法讲解
2016/03/01 PHP
Yii框架日志操作图文与实例详解
2019/09/09 PHP
JScript分割字符串示例代码
2013/09/04 Javascript
JavaScript—window对象使用示例
2013/12/09 Javascript
JavaScript实现的圆形浮动标签云效果实例
2015/08/06 Javascript
js数组如何添加json数据及js数组与json的区别
2015/10/27 Javascript
jQuery解决input元素的blur事件和其他非表单元素的click事件冲突问题
2016/08/15 Javascript
JS设置CSS样式的方式汇总
2017/01/21 Javascript
用vue和node写的简易购物车实现
2017/04/25 Javascript
vue父组件向子组件动态传值的两种方法
2017/11/11 Javascript
打通前后端构建一个Vue+Express的开发环境
2018/07/17 Javascript
详解如何模拟实现node中的Events模块(通俗易懂版)
2019/04/15 Javascript
[01:18]PWL开团时刻DAY4——圣剑与抢盾
2020/11/03 DOTA
[01:11:28]DOTA2-DPC中国联赛定级赛 RNG vs Phoenix BO3第一场 1月8日
2021/03/11 DOTA
用Python进行一些简单的自然语言处理的教程
2015/03/31 Python
Django自定义分页与bootstrap分页结合
2021/02/22 Python
python与C互相调用的方法详解
2017/07/14 Python
利用numpy实现一、二维数组的拼接简单代码示例
2017/12/15 Python
python矩阵/字典实现最短路径算法
2019/01/17 Python
Django实现文件上传下载
2019/10/06 Python
Python爬取网站图片并保存的实现示例
2021/02/26 Python
解析html5 canvas实现背景鼠标连线动态效果代码
2019/06/17 HTML / CSS
玩具反斗城葡萄牙官方商城:Toys"R"Us葡萄牙
2016/10/21 全球购物
德国汉莎航空中国官网: Lufthansa中国
2017/03/30 全球购物
高一历史教学反思
2014/01/13 职场文书
培训研修方案
2014/06/06 职场文书
2015年党员个人剖析材料
2014/12/18 职场文书
退货证明模板
2015/06/23 职场文书
2015年幼儿教育工作总结
2015/07/24 职场文书
MySQL数据库必备之条件查询语句
2021/10/15 MySQL
JavaScript实现队列结构过程
2021/12/06 Javascript
一起来看看Vue的核心原理剖析
2022/03/24 Vue.js
使用 DataAnt 监控 Apache APISIX的原理解析
2022/07/07 Servers