PythonStudy——多进程编程

进程

一个正在被运行的程序就称之为进程,是程序具体执行过程,一种抽象概念python

进程来自于操做系统 linux

多进程

进程和程序的区别

程序就是一堆计算机能够识别文件,程序在没有被运行就是躺在硬盘上的一堆二进制程序员

运行程序时,要从硬盘读取数据到内存中,CPU从内存读取指令并执行 ,编程

一旦运行就产生了进程 浏览器

一个程序能够屡次执行 产生多个进程,可是进程之间相互独立安全

当咱们右键运行了一个py文件时 ,其实启动的是python解释器,你的py文件实际上是看成参数传给了解释器 网络

阻塞 非阻塞 并行 并发 (重点)

阻塞 : 程序遇到io操做是就进入了阻塞状态 数据结构

本地IO input print sleep read write 并发

网络IO recv senddom

非阻塞: 程序正常运行中 没有任何IO操做 就处于非阻塞状态

阻塞 非阻塞 说的是程序的运行状态

并发: 多个任务看起来同时在处理 ,本质上是切换执行 速度很是快

并行: 多个任务真正的同时执行 必须具有多核CPU 才可能并行

并发 并行 说的是 任务的处理方式

三种状态的切换

程序员永恒的话题

提升效率

根本方法就是让程序尽量处于运行状态

减小IO 尽量多占用CPU时间

缓冲区就是用于减小IO操做的

进程的建立以及销毁 了解

菜谱: 就是程序

作菜的过程:就是进程

进程的两种使用方式 (重点)

1.直接实例化Process ,将要执行任务用target传入

 

2.继承Process类 ,覆盖run方法 将任务放入run方法中

import os
from multiprocessing import  Process
class MyProcess(Process):
   def __init__(self,name):
       super().__init__()
       self.name = name
   # 继承Procee覆盖run方法将要执行任务发到run中
   def run(self):
       print(self.name)
       print("子进程 %s running!" % os.getpid())
       print("子进程 %s over!" % os.getpid())

if __name__ == '__main__':
   # 建立时 不用再指定target参数了
   p = MyProcess("rose")
   p.start()
   print("父进程over!")

join函数 (重点)

Process的对象具有一个join函数

用于提升子进程优先级 ,使得父进程等待子进程结束

僵尸与孤儿进程 了解

孤儿进程

指的是,父进程先结束 ,而子进程还在运行着,

孤儿进程无害,有 其存在的必要性

例如:qq开启了浏览器,qq先退出了 浏览器应该继续运行

孤儿进程会被操做系统接管

僵尸进程

值得是,子进程已经结束了,可是操做系统会保存一些进程信息,如PID,运行时间等,此时这个进程就称之为僵尸进程

僵尸进程若是太多将会占用大量的资源,形成系统没法开启新新进程

linux 中有一个wai/waitpid 用于父进程回收子进程资源

python会自动回收僵尸进程

经常使用属性

# p.join() # 等待子进程结束
# p.terminate() # 终止进程
# print(p.name) # 进程的名称
# print(p.is_alive()) #是否存活
# p.terminate() # 与start同样 都是给操做系统发送指令 因此会有延迟
# print(p.is_alive())
# print(p.pid)
# print(p.exitcode) # 获取退出码

守护进程

    什么是守护进程

​    进程是一个正在运行的程序

​    守护进程也是一个普通进程

​    意思是一个进程能够守护另外一个进程

例如

​ 康熙要是一个进程的话,后宫佳丽都是守护者

​ 若是康熙挂了, 后宫佳丽们要陪葬

结论:

若是b是a的守护进程,a是被守护的进程,a要是挂了,b也就随之结束了

测试:

from multiprocessing import Process import time # 妃子的一辈子
def task(): print("入宫了.....") time.sleep(50) print("妃子病逝了......") if __name__ == '__main__': # 康熙登基了
print("登基了.....") # 找了一个妃子
p = Process(target=task) # 设置为守护进程 必须在开启前就设置好
p.daemon = True p.start() # 康熙驾崩了
time.sleep(3) print("故事结束了!")

使用场景:

​ 父进程交给了子进程一个任务,任务尚未完成父进程就结束了,子进程就没有继续执行的意义了

​ 例如:qq 接收到一个视频文件,因而开启了一个子进程来下载,若是中途退出了qq,下载任务就没必需要继续运行了

 

互斥锁

什么是互斥锁

互斥锁 互相排斥的锁,我在这站着你就别过来,(若是这个资源已经被锁了,其余进程就没法使用了)

须要强调的是: 锁 并非真的把资源锁起来了,只是在代码层面限制你的代码不能执行

为何须要互斥锁:

并发将带来资源的竞争问题
当多个进程同时要操做同一个资源时,将会致使数据错乱的问题
以下列所示:

解决方案1:

​ 加join,
​ 弊端 1.把本来并发的任务变成了穿行,避免了数据错乱问题,可是效率下降了,这样就不必开子进程了
​ 2.本来多个进程之间是公平竞争,join执行的顺序就定死了,这是不合理的

解决方案2:

​ 就是给公共资源加锁,互斥锁
​ 互斥锁 互相排斥的锁,我在这站着你就别过来,(若是这个资源已经被锁了,其余进程就没法使用了)

锁 并非真的把资源锁起来了,只是在代码层面限制你的代码不能执行

锁和join的区别:​

1. join是固定了执行顺序,会形成父进程等待子进程

​ 锁依然是公平竞争谁先抢到谁先执行,父进程能够作其余事情

​ 2.最主要的区别:
​ join是把进程的任务所有串行
​ 锁能够锁任意代码 一行也能够 能够本身调整粒度


案例

from multiprocessing import Process,Lock import time,random def task1(lock): # 要开始使用了 上锁
lock.acquire() #就等同于一个if判断
print("hello iam jerry") time.sleep(random.randint(0, 2)) print("gender is boy") time.sleep(random.randint(0, 2)) print("age is 15") # 用完了就解锁
lock.release() def task2(lock): lock.acquire() print("hello iam owen") time.sleep(random.randint(0,2)) print("gender is girl") time.sleep(random.randint(0,2)) print("age is 48") lock.release() def task3(lock): lock.acquire() print("hello iam jason") time.sleep(random.randint(0,2)) print("gender is women") time.sleep(random.randint(0,2)) print("age is 26") lock.release() if __name__ == '__main__': lock = Lock() p1 = Process(target=task1,args=(lock,)) p2 = Process(target=task2,args=(lock,)) p3 = Process(target=task3,args=(lock,)) p1.start() # p1.join()
 p2.start() # p2.join()
 p3.start() # p3.join()

# print("故事结束!")

# 锁的伪代码实现

# if my_lock == False: # my_lock = True # #被锁住的代码
my_lock = False 解锁

注意1: 不要对同一把执行多出acquire 会锁死致使程序没法执行 一次acquire必须对应一次release

l = Lock()
l.acquire()
print("抢到了!")
l.release()
l.acquire()
print("强哥毛线!")

注意2:想要保住数据安全,必须保住全部进程使用同一把锁

 

IPC

​ 进程间通信

​ 通信指的就是交换数据

​ 进程之间内存是相互隔离的,当一个进程想要把数据给另一个进程,就须要考虑IPC

方式

​ 管道

       只能单向通信,数据都是二进制

​ 文件

       在硬盘上建立共享文件

​ 缺点:速度慢

​ 优势:数据量几乎没有限制

​socket

​ 编程复杂度较高

​ 共享内存:必须由操做系统来分配 要掌握的方式*****

​ 优势: 速度快

​ 缺点: 数据量不能太大

共享内存的方式

Manager类

​ Manager提供不少数据结构 list dict等等

​ Manager所建立出来的数据结构,具有进程间共享的特色

from multiprocessing import Process,Manager,Lock import time def task(data,l): l.acquire() num = data["num"] # time.sleep(0.1) data["num"] = num - 1 l.release() if __name__ == '__main__': # 让Manager开启一个共享的字典
m = Manager() data = m.dict({"num":10}) l = Lock() for i in range(10): p = Process(target=task,args=(data,l)) p.start() time.sleep(2) print(data)

​ 须要强调的是 Manager建立的一些数据结构是不带锁的 可能会出现问题

Queue队列

帮咱们处理了锁的问题 重点

​ 队列是一种特殊的数据结构,先存储的先取出 就像排队 先进先出

​ 相反的是堆栈,先存储的后取出, 就像衣柜 桶装薯片 先进后出

​ 扩展:

​ 函数嵌套调用时 执行顺序是先进后出 也称之为函数栈

​ 调用 函数时 函数入栈 函数结束就出栈

from multiprocessing import Queue # 建立队列 不指定maxsize 则没有数量限制
q = Queue(3) # 存储元素 # q.put("abc") # q.put("hhh") # q.put("kkk")

# print(q.get()) # q.put("ooo") # 若是容量已经满了,在调用put时将进入阻塞状态 直到有人从队列中拿走数据有空位置 才会继续执行

#取出元素 # print(q.get())# 若是队列已经空了,在调用get时将进入阻塞状态 直到有人从存储了新的数据到队列中 才会继续

# print(q.get()) # print(q.get())


#block 表示是否阻塞 默认是阻塞的 # 当设置为False 而且队列为空时 抛出异常
q.get(block=True,timeout=2) # block 表示是否阻塞 默认是阻塞的 # 当设置为False 而且队列满了时 抛出异常 # q.put("123",block=False,) # timeout 表示阻塞的超时时间 ,超过期间仍是没有值或仍是没位置则抛出异常 仅在block为True有效

 

 

生产者消费者模型 重点

## 是什么

​ 模型 就是解决某个问题套路

​ 产生数据的一方称之为生产者

​ 处理数据的一方称之为消费者

​ 例如: 爬虫 生活中处处都是这种模型

​ 饭店 厨师就是生产者 你吃饭的人就是消费者

## 生产者和消费者出啥问题了? 解决什么问题

​ 生产者和消费,处理速度不平衡,一方快一方慢,致使一方须要等待另外一方

## 生产者消费者模型解决这个问题的思路: 怎么解决

​ 本来,双方是耦合 在一块儿,消费必须等待生产者生成完毕在开始处理, 反过来

​ 若是消费消费速度太慢,生产者必须等待其处理完毕才能开始生成下一个数据

### 解决的方案:

​ 将双方分开来.一专门负责生成,一方专门负责处理

​ 这样一来数据就不能直接交互了 双方须要一个共同的容器

​ 生产者完成后放入容器,消费者从容器中取出数据

​ 这样就解决了双发能力不平衡的问题,作的快的一方能够继续作,不须要等待另外一方

案例:

def eat(q): for i in range(10): # 要消费
rose = q.get() time.sleep(random.randint(0, 2)) print(rose,"吃完了!") # 生产任务
def make_rose(q): for i in range(10): # 再生产
time.sleep(random.randint(0, 2)) print("第%s盘青椒肉丝制做完成!" % i) rose = "第%s盘青椒肉丝" % i # 将生成完成的数据放入队列中
q.put(rose) if __name__ == '__main__': # 建立一个共享队列
q = Queue() make_p = Process(target=make_rose,args=(q,)) eat_p = Process(target=eat,args=(q,)) make_p.start() eat_p.start()
相关文章
相关标签/搜索