Python多进程

多进程

什么是多任务

什么叫做多任务呢?简单的说,就是操作系统(OS)可以同时运行多个任务。如你可以一边浏览网页,一边听着歌,同时还可以使用画板画着画,这个就是多任务。其实我们的操作系统就是多任务。
单核CPU:
1、时间片轮换
2、优先级别调度
多核CPU:
1、并发
2、并行

Linux下fork实现多进程

进程:

       ◆编写完的代码,在没有运行的情况下,称之为程序。
       ◆正在运行的代码,就成为了进程。
       ◆进程,除了包含代码外,还需要运行环境等,所以和程序是存在区别的。
       ◆进行多进程操作时,肯定会存在一个主进程。

fork()方法

Python在os模块上封装了常用的系统调用,其中就包括了fork,我们可以很轻松地在Python代码中创建进程。fork() 函数可以获得系统中进程的PID ( Process ID ),返回0则为子进程,否则就是父进程,然后可以据此对运行中的进程进行操作;但是os.fork()只能在Linux下运行,可以使用os.getpid()获取该进程的进程号,os.getppid()获取子进程的父进程进程号。

import os
pid = os.fork()
if pid == 0:
  while True:
    print("执行子进程!我的进程id:%s,我的父进程id:%s" % (os.getpid(),os.getppid()))
else:
  while True:
    print("执行主进程!我的进程id:%s,我的子进程id:%s" % (os.getpid(),pid))

#运行结果
>>>执行子进程!我的进程id:2978,我的父进程id:2977
>>>执行主进程!我的进程id:2977,我的子进程id:2978
>>>执行主进程!我的进程id:2977,我的子进程id:2978
>>>执行子进程!我的进程id:2978,我的父进程id:2977
>>>...

#假如多进程中有全局变量的使用
import os
pid = os.fork()
num = 0
if pid == 0:
  num +=1
  print("子进程" + num)
else:
  num += 1
  print("父进程" + num)
#运行结果
>>>子进程1
>>>父进程1

#当多个fork存在时的运行结果
import os 
pid1 = os.fork()
if pid1 == 0:
  print("我是进程1")
else:
  print("我是进程2")
pid2 = os.fork()
if pid2 == 0:
  print("我是进程3")
else:
  pirnt("我是进程4")
#运行结果
>>>我是进程1
>>>我是进程3
>>>我是进程4
>>>我是进程2
>>>我是进程3
>>>我是进程4

观察运行结果发现:
       ◆两个进程的运行的顺序是无序的;
       ◆进程之间的数据无法共享,是各自拥有的一份;
       ◆当程序遇到fork时程序会被分成一个子进程一个主进程,然后再次遇到fork子进程又被分成一个主进程和一个子进程,主进程也被分成一个子进程和主进程,就产生了上面的第三个运行结果;

fork的工作原理

函数多进程

multiprocessing引入多进程

fork()方法只能使用在linux系统下(tornado框架的高效性就是使用一个单线程通过fork()实现同时服务多个客户端)
python中在linux和windows下都能使用的多线程是multiprocessing中的Process类,通过Process实现一个多进程

# 引入模块
from multiprocessing import Process
import time
# 定义一个函数,将来定义成一个进程
def pro1(msg):
  for i in range(5):
    print("进程%s正在运行" % msg )
    time.sleep(1)
  else:
    print("进程%s已结束" % msg)
def pro2(msg):
  for i in range(10):
    print("进程%s正在运行" % msg )
    time.sleep(1)
  else:
    print("进程%s已结束" % msg)

# 主进程开始,然后启动子进程
if __name__ == "__main__":
  """
  1、多进程必须需要主进程才能执行,python中__main__就是主进程的位置
  2、将定义好的函数转换成进程,只需要将Process类实例化,必备属性有:
    ①target:Process的一个属性,值是要转换成子进程的函数名
    ②args:它必须是一个元组,保存所有要用到的参数,若只有一个参数,写成(val,)的形式
  """
  print("————————主进程启动————————")
  bilibili = Process(target=pro1,args=("哔哩哔哩",),name="哔哩哔哩干杯")
  dnf = Process(target=pro2,args=("掉线城与勇士",),name="CNM给我出时光鞋")
  # 开启子进程
  print(bilibili.name)
  bilibili.start()
  # 这里可以使用一下进程对象的terminate方法,终止一个进程
  if bilibili.is_alive():
    bilibili.terminate()
    print("哔哩哔哩已结束")
  print(dnf.name)
  dnf.start()
  # 设置主进程等待子进程结束在结束自己本身,当有多个进程时
  # 等待哪个进程就会在哪个进程结束后向下执行,
  bilibili.join()
  print("————————主进程结束————————")

# 运行结果
D:\python软件\python.exe D:/python_test/re_test.py
————————主进程启动————————
哔哩哔哩干杯
哔哩哔哩已结束
CNM给我出时光鞋
————————主进程结束————————
进程掉线城与勇士正在运行
进程掉线城与勇士正在运行
进程掉线城与勇士正在运行
进程掉线城与勇士正在运行
进程掉线城与勇士正在运行
进程掉线城与勇士正在运行
进程掉线城与勇士正在运行
进程掉线城与勇士正在运行
进程掉线城与勇士正在运行
进程掉线城与勇士正在运行
进程掉线城与勇士已结束

Process finished with exit code 0
总结:

1、主进程是必须的
2、创建进程实例时target目标是函数名不是调用函数
3、args属性必须是一个元组,只有一个参数需要时写成(val,)
4、termanite()终止方法,结束进程
5、多个子进程存在时,设置主进程等待哪个子进程,就在哪个子进程结束后继续向下执行主进程

类的多进程

创建一个子进程类的步骤

1、定义一个类,继承自Process父类
2、如果需要父类中的某些属性使用supper().__init__()或Process.__init__()调用
3、重写Process父类的run方法,在run方法中写入需要执行的进程

from multiprocessing import Process
class MyProcess(Process):
  def __init__(self,name,msg):
    super().__init__(name=name)
    self.msg = msg
  #重写run方法
  def run(self):
    print(self.name)
    print("子进程%s开始运行" % self.msg)

# 主进程入口
if __name__ == "__main__":
  print("————————主进程开始————————")
  # 定义出子进程的实例,并执行子进程
  for i in range(5):
    m = MyProcess("bilibli","哔哩哔哩动画")
    m.start()
    m.join()
  print("————————主进程结束————————")

#运行结果
D:\python软件\python.exe D:/python_test/re_test.py
————————主进程开始————————
bilibli
子进程哔哩哔哩动画开始运行
bilibli
子进程哔哩哔哩动画开始运行
bilibli
子进程哔哩哔哩动画开始运行
bilibli
子进程哔哩哔哩动画开始运行
bilibli
子进程哔哩哔哩动画开始运行
————————主进程结束————————

Process finished with exit code 0

进程池申请异步

进程池概念

当我们有很多大量的进程需要执行时(例如多个用户访问服务器,每个用户都是一个进程);
此时若继续使用Process创建多进程,需要多次创建,大大加重了内存的使用和代码量;
使用进程池Pool保存进程,能提前创建规定数量的进程,通过遍历一次得到多个进程;
达到并发并行的效果

定义一个简单的进程池
"""
按照顺序用进程池申请异步的方法——apply_async()
"""
#引入需要的模块
from multiprocessing import Pool
# 定义进程对象的目标函数
def run(msg):
  print("进程%s运行了" %msg)
#主进程入口
if __name__ == "__main__":
  print("主进程开始")
  # 定义一个进程池,池中保存3个进程
  p = Pool(3)
  # 遍历进程池获取一次获取3个进程
  for i in range(10):
    #进程池实例的apply_async()方法申请异步,实现并发并行,参数有target,args,name等
    p.apply_async(run,("开启session",))

  #执行完毕后一定要关闭进程池
  p.close()
  #这里是重点,进程池中子进程类似于守护进程,
  #当主进程结束后,子进程自动结束,所以必须设置主进程等待
  p.join()
  print("主进程结束")

#运行结果
D:\python软件\python.exe D:/_web_project/tetetetetet.py
主进程开始
进程开启session运行了
进程开启session运行了
进程开启session运行了
进程开启session运行了
进程开启session运行了
进程开启session运行了
进程开启session运行了
进程开启session运行了
进程开启session运行了
进程开启session运行了
主进程结束

Process finished with exit code 0
总结重点

1、定义进程池实例时要设定池中一次性存放几个子进程;
2、执行结果是一次出现3个然后出现3次,最后一次只出现了一个;
3、子进程执行完毕后,必须关闭进程池;
4、主进程必须等待进程池中的子进程执行完毕后再结束。【守护进程:跟随主进程的结束而结束】

进程中的数据传递和数据共享

在上面的案例中可以看出,进程间的数据都是单独的一份,不会发生影响;但是进程之间的数据并不是绝对隔离的,可以实现使用Queue进行进程的数据传递,使用Manager实现进程间的数据共享。

Queue数据传递

Queue是一种特殊的先进先出的线性数据表,一般的数据队列都有大小限制,等待超时时间需要自己定义,常用方法有:
     ◆put()放入数据到队列中
     ◆get()从队列中取出数据
     ◆empty()清空队列

from multiprocessing import Process,Queue
#子进程
def put_pro(q,n):
  #向队列添加数据
  q.put("第%s个进程添加了一个数据" % n)
#主进程
def get_pro(q):
  print("————————————start————————————")
  for i in range(5):
    p = Process(target=put_pro,args=(q,i+1))
    p.start()
    #是否收到子进程的数据
    print(q.get())
    p.join()
  print("————————————end—————————————")
#程序入口
if __name__ == "__main__":
  #定义一个队列实例
  q = Queue()
  #启用多进程
  get_pro(q)

>>>运行结果
D:\python软件\python.exe D:/_web_project/tetetetetet.py
————————————start————————————
第1个进程添加了一个数据
第2个进程添加了一个数据
第3个进程添加了一个数据
第4个进程添加了一个数据
第5个进程添加了一个数据
————————————end—————————————

Process finished with exit code 0
Manager实现进程间数据共享

from multiprocessing import Manager 数据管理器类,一个管理器类可以创建用于进程间数据共享的各种对象,例如:dict,list,queue,Array,Lock等,有了管理器就可以根据需要定义数据类型,减少局限性

from multiprocessing import Process,Manager
# 定义两个子进程,分别向全局变量中添加数据
def add_one(d,ls,n):
  d["name%s" %n] = "进程一%s" % n
  ls.append(n)
  print("进程1执行了",d,ls)
def add_two(d,ls,n):
  d["name%s" %n] = "进程二%s" % n
  ls.append(n)
  print("进程2执行了",d,ls)
def run(d,ls):
  for i in range(3):
    p1 = Process(target=add_one,args=(d,ls,i))
    p1.start()
    p1.join()
    p2 = Process(target=add_two,args=(d,ls,i))
    p2.start()
    p2.join()
  #输出执行完毕的全局变量
  print(d,ls)
if __name__ == "__main__":
  #定义几个全局的Manager变量
  d = Manager().dict()
  ls = Manager().list()
  run(d,ls)

>>>运行结果
D:\python软件\python.exe D:/_web_project/tetetetetet.py
进程1执行了 {'name0': '进程一0'} [0]
进程2执行了 {'name0': '进程二0'} [0, 0]
进程1执行了 {'name0': '进程二0', 'name1': '进程一1'} [0, 0, 1]
进程2执行了 {'name0': '进程二0', 'name1': '进程二1'} [0, 0, 1, 1]
进程1执行了 {'name0': '进程二0', 'name1': '进程二1', 'name2': '进程一2'} [0, 0, 1, 1, 2]
进程2执行了 {'name0': '进程二0', 'name1': '进程二1', 'name2': '进程二2'} [0, 0, 1, 1, 2, 2]
{'name0': '进程二0', 'name1': '进程二1', 'name2': '进程二2'} [0, 0, 1, 1, 2, 2]
Process finished with exit code 0
————————————————————————————————————————————————————————————————————
#如果将程序中的管理器字典和管理器列表变成{}和[],运行结果为
进程1执行了 {'name0': '进程一0'} [0]
进程2执行了 {'name0': '进程二0'} [0]
进程

1执行了 {'name1': '进程一1'} [1]
进程2执行了 {'name1': '进程二1'} [1]
进程1执行了 {'name2': '进程一2'} [2]
进程2执行了 {'name2': '进程二2'} [2]
{} []

python的多进程操作大概就是这样,对于python来说由于cpython解释器全局锁的存在,python的多线程并不能发挥它的威力,熟练掌握多进程是很重要的

来自P站画师:wlop
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容

  • 一、进程的概念 相信很多同学都听说过windows、linux,MacOS都是多任务,多用户的操作系统。那什么是多...
    转身后的那一回眸阅读 926评论 0 1
  • 现在, 多核CPU已经非常普及了, 但是, 即使过去的单核CPU, 也可以执行多任务。 CPU执行代码都是顺序执行...
    LittlePy阅读 4,740评论 0 3
  • 进程的基本概念 进程是程序的一次执行,每个进程都有自己的地址空间,内存,数据栈以及其他记录其运行轨迹的辅助数据。多...
    XYZeroing阅读 1,178评论 0 13
  • Python 多进程 multiprocessing.Pool类详解 multiprocessing模块 mult...
    很少更新了阅读 8,943评论 2 14
  • 前言:为什么有人说 Python 的多线程是鸡肋,不是真正意义上的多线程? 看到这里,也许你会疑惑。这很正常,所以...
    猴哥爱读书阅读 51,520评论 6 69