多道技术

1.空间上的复用

多个程序公用一套计算机硬件

2.时间上的复用 cpu 切换程序+保存程序状态

1.当一个程序遇到IO操作,操作系统会剥夺该程序的cpu执行权限(提高了cpu的利用率,并且不影响程序的执行效率

2.当一个程序长时间占用cpu 操作系统也会剥夺该程序的cpu执行权限(降低了程序的执行效率)

进程

进程是正在运行的程序

进程是操作系统中最基本、重要的概念。是多道程序系统出现后,为了刻画系统内部出现的动态情况,描述系统内部各道程序的活动规律引进的一个概念,所有多道程序设计操作系统都建立在进程的基础上。
ps: 同一个程序多次执行,就会在操作系统中出现两个进程,所以我们可以同时运行一个软件,分别做不同的事情也不会混乱

进程调度

时间片转轮法

基本思路是让每个进程在就绪队列中的等待时间与享受服务的时间成比例。在时间片轮转法中,需要将CPU的处理时间分成固定大小的时间片,例如,几十毫秒至几百毫秒。如果一个进程在被调度选中之后用完了系统规定的时间片,但又未完成要求的任务,则它自行释放自己所占有的CPU而排到就绪队列的末尾,等待下一次调度。同时,进程调度程序又去调度当前就绪队列中的第一个进程。

并行与并发

并行: 多个程序同时运行,并且每个程序有单独的cpu运算(只能在多核计算机上实现)

并发:多个程序看起来是同时运行,实际上是由一个cpu来回切换执行的

**并行**是从微观上,也就是在一个精确的时间片刻,有不同的程序在执行,这就要求必须有多个处理器。
**并发**是从宏观上,在一个时间段上可以看出是同时执行的,比如一个服务器同时处理多个session。

同步异步 / 阻塞非阻塞

在程序运行的过程中,由于被操作系统的调度算法控制,程序会进入几个状态:就绪,运行和阻塞

同步/异步

表示的是任务的提交方式

同步:任务提交后 原地等待任务的执行结果并拿到返回结果后才走,期间不会做任何事(程序层面表现的是卡住了)

异步:任务提交后 不再原地等待,而是继续执行下一行代码(提交的任务结果还是要的,但是是用骐达方式获取)

import time

def func(name):
print('%s runing'%name)
time.sleep(3)

func('waller') # 同步 任务提交后原地等待函数运行结束后再走下面的代码
print('procedure over')

阻塞/非阻塞

表示的是进程的运行状态

阻塞: 指进程的阻塞态

非阻塞: 指进程的就绪态 运行态

multiprocessing模块

multiprocessing模块用来开启子进程,并在子进程中执行我们定制的任务(比如函数)
multiprocessing模块的功能众多:支持子进程、通信和共享数据、执行不同形式的同步,提供了Process、Queue、Pipe、Lock等组件。

Process 创建进程的类

语法

from multiprocessing import Process

代码块

if __name__ == __main___: #
p = Process(target=调用对象, args=调用对象的位置参数元组) # 创建一个进程对象
代码块
p.start() # 告诉操作系统帮你创建一个进程 ​
注意: windows创建进程一定要在 if __name__ == __main___: 代码块内创建,否则会报错

windows创建进程会将代码已模块的方式,从上往下执行一遍,linux会直接将代码完完整整的拷贝一份
创建进程就是在内存中重新开辟了一块内存空间,将运行产生的代码丢进去,一个进程对应砸死内存就是一块独立的内存空间
进程进程之间的数据时隔离的,无法直接交互,但是可以通过某些技术实现间接交互

参数介绍

Process源码:

class Process(object):
def __init__(self, group=None, target=None, name=None, args=(), kwargs={}):
...
# group参数未使用,值始终为None
# target表示调用对象,即子进程要执行的任务
# args表示调用对象的位置参数元组,如:args=(1,2,'egon',)
# kwargs表示调用对象的字典,kwargs={'name':'egon','age':18}
# name为子进程的名称

方法介绍

p.start():创建启动进程,并调用该子进程中的p.run()
p.run():进程启动时运行的方法,正是它去调用target指定的函数,我们自定义类的类中一定要实现该方法
p.terminate():强制终止进程p,不会进行任何清理操作,如果p创建了子进程,该子进程就成了僵尸进程,使用该方法需要特别小心这种情况。如果p还保存了一个锁那么也将不会被释放,进而导致死锁
p.is_alive():如果p仍然运行,返回True
p.join([timeout]):主线程等待p终止(强调:是主线程处于等的状态,而p是处于运行的状态)。timeout是可选的超时时间,需要强调的是,p.join只能join住start开启的进程,而不能join住run开启的进程

创建进程的两种方式

from multiprocessing import Process
import time

def func(name):
print('%s running'%name)
time.sleep(3)
print('over')
print('w')

if __name__ == '__main__':
# 创建进程对象
p = Process(target=func, args=('waller',)) # target=func 是在p进程内存空间中调用执行了 func 函数
# 告诉操作系统创建一个进程 开辟p进程的内存空间
p.start() # 启动p进程
print('主进程') # 异步
from multiprocessing import Process
import time

class MyProcess(Process):
def __init__(self, name):
super(MyProcess, self).__init__()
self.name = name
def run(self):
print('%s running'%self.name)
time.sleep(3)
print('over')

if __name__ == '__main__':
p = MyProcess('waller')
p.start() # 自动运行 run 方法
print('主进程')

join

主进程等待子进程运行结束后再运行

from multiprocessing import Process
import time

def func(name, i):
print('%s 进程 running'%name)
time.sleep(i)
print('%s 进程 over'%name)

if __name__ == '__main__':
p1 = Process(target=func, args=('A',1))
p2 = Process(target=func, args=('B',2))
p3 = Process(target=func, args=('C',3))
start_time = time.time()
p1.start()
p2.start()
p3.start()
p2.join()
p1.join()
p3.join()
print('主进程')
print(time.time() - start_time)
'''
B 进程 running
A 进程 running
C 进程 running
A 进程 over
B 进程 over
C 进程 over
主进程
3.1728098392486572

'''

进程之间数据时隔离的

from multiprocessing import Process

x = 100
def func():
global x
x = 200
print(x)

if __name__ == '__main__':
p = Process(target=func)
p.start()
print(x)
>>>
100
200

进程对象及其他方法

os.getpid() 当前进程的pid
os.getppid() 当前进程的父进程pid
p.terminate() 杀死p进程,告诉操作系统帮你杀死一个进程
p.is_alive() 判断p进程是否存活,返回bool值
def func(name):
print('%s 进程 running'%name, '%s 进程 %s'%(name, os.getpid()), '父进程%s'%os.getppid())
time.sleep(3)
print('%s 进程 over'%name)

if __name__ == '__main__':
p = Process(target=func, args=('w',))
p.start()
print('父进程%s'%os.getpid(), '父父进程%s'%os.getppid())
>>>
父进程5224 父父进程10332
w 进程 running w 进程 1912 父进程5224
w 进程 over
from multiprocessing import Process
import os
import time

def func(name):
print('%s 进程 running'%name, '%s 进程 %s'%(name, os.getpid()), '父进程%s'%os.getppid())
time.sleep(3)
print('%s 进程 over'%name)

if __name__ == '__main__':
p = Process(target=func, args=('w',))
p.start()
print(p.is_alive()) # True
p.terminate() # 告诉操作系统杀死p进程
time.sleep(1) # 操作系统的速度没有代码运行的速度快,所以需要睡眠
print(p.is_alive()) # False
print('父进程%s'%os.getpid(), '父父进程%s'%os.getppid())

僵尸进程与孤儿进程

父进程回收子进程资源的两种方式

1.join方法

2.父进程进程死亡

所有程序都将步入僵尸进程

孤儿进程: 子进程没死,父进程意外死亡

守护进程

会随着主进程的结束而结束

p.daemon=True
一定要在p.start()前设置,设置p为守护进程,禁止p创建子进程,并且父进程代码执行结束,p即终止运行

注意:进程之间是互相独立的,主进程代码运行结束,守护进程随即终止

from multiprocessing import Process
import time

def func(name):
print('%s running'%name)
time.sleep(3)
print('over')


if __name__ == '__main__':
p = Process(target=func, args=('waller',))
p.daemon = True
p.start()
time.sleep(1) # 由于是守护进程,主进程运行结束,子进程就被清空,所以要等待操作系统创建子进程并运行
print('主进程')

互斥锁

互斥锁
当多个进程操作同一份数据的时候 会造成数据的错乱
这个时候必须加锁处理
将并发变成串行
虽然降低了效率但是提高了数据的安全
注意:
1.锁不要轻易使用 容易造成死锁现象
2.只在处理数据的部分加锁 不要在全局加锁 锁必须在主进程中产生 交给子进程去使用  
from multiprocessing import Process, Lock
import json
import time

# 查票
def search(name):
with open('data', 'r', encoding='utf-8') as f:
data = f.read() # 读出的是json模式的数据
t = json.loads(data).get('ticket')
print('用户%s查询余票为:%s'%(name, t))
# search('waller')

# 买票
def buy(name):
with open('data', 'r', encoding='utf-8') as f:
data_json = f.read() # 读出的是json模式的数据
data = json.loads(data_json)
t = data.get('ticket')
# 模拟抢票时间
time.sleep(2)
if not t > 0 :
print('已无票')
return
t -= 1
data['ticket'] = t
# 跟新数据
with open('data', 'w', encoding='utf-8') as f:
json.dump(data, f) # 将更新后的数据序列化到数据库中
print('用户%s,购票成功'%name)

# 在进程中调用 search 与 buy 函数
def run(name, mutex):
search(name)
mutex.acquire() # 抢锁
buy(name) # 被锁的函数
mutex.release() # 释放锁


if __name__ == '__main__':
# 生成一把锁
mutex = Lock()
# 创建5个进程
for i in range(5):
p = Process(target=run, args=('waller',mutex))
p.start()

进程间的通信

Queue 模块

# 创建队列对象
q = Queue(5) # 括号内可以传参数,表示是这个队列的最大存储,不传参数默认最大存储
q.put() # 往队列中添加数据 当队列满了之后,再放数据,不会报错,会原地等待,知道队列中有一个空位置出现
q.full() # 判断队列是否满了,返回bool值
q.get() # 从队列中取值 当队列中的值取完后再取,不会报错,程序会阻塞,直到队列中再放入一个值
q.empty() # 判断队列的值是否取完
q.get_nowait() # 取值 当队列中没有值可取时,不等待直接报错
from multiprocessing import Queue


# 创建队列对象
q = Queue(5) # 括号内可以传参数,表示是这个队列的最大存储,不传参数默认最大存储
# 往队列中添加数据
q.put(1)
q.put(2)
# 判断队列是否满了,返回bool值
print(q.full()) # >>> False
q.put(3)
q.put(4)
q.put(5)
print(q.full()) # >>> True
q.put(6) # 当队列满了之后,再放数据,不会报错,会原地等待,知道队列中有一个空位置出现

# 从队列中取值
print(q.get())
print(q.get())
# 判断队列的值是否取完
print(q.empty()) # >>> False
print(q.get())
print(q.get())
print(q.get())
print(q.get()) # 当队列中的值取完后再取,不会报错,程序会阻塞,直到队列中再放入一个值

print(q.get_nowait()) # 取值 当队列中没有值可取时,不等待直接报错
full()
get_nowait()
empty()
都不适合用于多进程的情况判断,
可以把多进程之间的运行看做是异步,当在判断的那一刻,拿到判断结果的一瞬间,
队列可能会又存入或取出一个值,不准确.

进程间通信IPC机制

from multiprocessing import Process, Queue

# 情况一: 子进程放数据,父进程取数据
def sub(q):
q.put('hello')

if __name__ == '__main__':
# 创建队列对象
q = Queue()
p = Process(target=sub, args=(q,))
p.start()
print(q.get()) # >>> hello

# def sub(q):
# print(q.get()) # >>> hello
#
# if __name__ == '__main__':
# # 创建队列对象
# q = Queue()
# p = Process(target=sub, args=(q,))
# p.start()
# q.put('hello')


# 情况二: 两个子进程间存取
def sub1(q):
q.put('hello')

def sub2(q):
print(q.get()) # >>> hello

if __name__ == '__main__':
q = Queue()
s1 = Process(target=sub1, args=(q,))
s2 = Process(target=sub2, args=(q,))
s1.start()
s2.start()

生产者消费者模型

 1.生产者消费者模型

在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题。该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度。

2.为什么要使用生产者和消费者模式

在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,
那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题于是引入了生产者和消费者模式。

3.什么是生产者消费者模式

生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,
直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。

基于队列实现生产者消费者模型
生产者消费者模型总结

#程序中有两类角色
一类负责生产数据(生产者)
一类负责处理数据(消费者) #引入生产者消费者模型为了解决的问题是:
平衡生产者与消费者之间的工作能力,从而提高程序整体处理数据的速度 #如何实现:
生产者<-->队列<——>消费者
#生产者消费者模型实现类程序的解耦和
产生原因及解决办法
原因是:生产者p在生产完后就结束了,但是消费者c在取空了q之后,则一直处于死循环中且卡在q.get()这一步。 解决方式:
  普通方法:无非是让生产者在生产完毕后,往队列中再发一个结束信号,这样消费者在接收到结束信号后就可以break出死循环
  JoinableQueue 方法: 生产者生产的每个数据上都做一个标记,消费者每 q.get() 取一个值,都用 q.task_done() 标记一次,q.join()感知队列中的数据全部处理完毕,再最终结束
 
 
from multiprocessing import Process, Queue
import time
import random

# 生产者
def producer(name, food, q):
for i in range(5):
data = '%s生产%s%s'%(name, food, i+1)
time.sleep(random.random())
q.put(data)
print(data)

# 消费者
def consumer(name, q):
while True:
data = q.get() # 程序停在此处
if data == None:break
print('%s吃了%s'%(name, data))
time.sleep(random.random())


if __name__ == '__main__':
q = Queue()
p1 = Process(target=producer, args=('小明', '包子', q))
p2 = Process(target=producer, args=('小刘', '馒头', q))
c1 = Process(target=consumer, args=('哈哈',q))
c2 = Process(target=consumer, args=('嘻嘻',q))
p1.start()
p2.start()
c1.start()
c2.start()
# 此时 生产者已经不再生产数据,但消费者还在取数据,卡在data = q.get()
# 解决办法 在生产者在生产完毕后,往队列中再发一个结束信号,这样消费者在接收到结束信号后就可以break出死循环
p1.join()
p2.join()
q.put(None)
q.put(None) # 有两个消费者

代码

 

调用 JoinableQueue 模块

#JoinableQueue([maxsize]):这就像是一个Queue对象,但队列允许项目的使用者通知生成者项目已经被成功处理。通知进程是使用共享的信号和条件变量来实现的。
#方法介绍:
JoinableQueue的实例p除了与Queue对象相同的方法之外还具有:
q.task_done():使用者使用此方法发出信号,表示q.get()的返回项目已经被处理。如果调用此方法的次数大于从队列中删除项目的数量,将引发ValueError异常
q.join():生产者调用此方法进行阻塞,直到队列中所有的项目均被处理。阻塞将持续到队列中的每个项目均调用q.task_done()方法为止
 
from multiprocessing import Process, JoinableQueue
import time
import random


# 生产者
def producer(name, food, q):
for i in range(5):
data = '%s生产%s%s' % (name, food, i + 1)
time.sleep(random.random())
q.put(data)
print(data)


# 消费者
def consumer(name, q):
while True:
data = q.get() # 程序停在此处
if data == None: break
print('%s吃了%s' % (name, data))
time.sleep(random.random())
q.task_done()


if __name__ == '__main__':
q = JoinableQueue()
p1 = Process(target=producer, args=('小明', '包子', q))
p2 = Process(target=producer, args=('小刘', '馒头', q))
c1 = Process(target=consumer, args=('哈哈', q))
c2 = Process(target=consumer, args=('嘻嘻', q))
p1.start()
p2.start()
c1.daemon = True
c2.daemon = True
c1.start()
c2.start()
# 此时 生产者已经不再生产数据,但消费者还在取数据,卡在data = q.get()
# 解决办法 在生产者在生产完毕后,往队列中再发一个结束信号,这样消费者在接收到结束信号后就可以break出死循环
p1.join()
p2.join()
q.join() # 等到队列中的数据全取出

代码

 

进程 multiprocessing Process join Lock Queue的更多相关文章

  1. python多进程的理解 multiprocessing Process join run

    最近看了下多进程. 一种接近底层的实现方法是使用 os.fork()方法,fork出子进程.但是这样做事有局限性的.比如windows的os模块里面没有 fork() 方法. windows:.lin ...

  2. 进程之 Process join方法其他属性与进程Queue

    Process join方法 以及其他属性 在主进程运行过程中如果想并发地执行其他的任务,我们可以开启子进程,此时主进程的任务与子进程的任务分两种情况 情况一:在主进程的任务与子进程的任务彼此独立的情 ...

  3. 多进程 multiprocessing 模块进程并发Process;Pool ;Queue队列 、threading模块;

    multiprocessing 模块中的 Process类提供了跨平台的多进程功能,在windows和linux系统都可以使用. 1.首先要实例化一个类,传入要执行的函数. 实例名 = Process ...

  4. 机器学习进阶-目标追踪-SSD多进程执行 1.cv2.dnn.readnetFromCaffe(用于读取已经训练好的caffe模型) 2.delib.correlation_tracker(生成追踪器) 5.cv2.writer(将图片写入视频中) 6.cv2.dnn.blobFromImage(图片归一化) 10.multiprocessing.process(生成进程)

    1. cv2.dnn.readNetFromCaffe(prototxt, model)  用于进行SSD网络的caffe框架的加载 参数说明:prototxt表示caffe网络的结构文本,model ...

  5. Python 浅析线程(threading模块)和进程(process)

    线程是操作系统能够进行运算调度的最小单位.它被包含在进程之中,是进程中的实际运作单位.一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务 进程与线程 什么 ...

  6. Python之路(第三十七篇)并发编程:进程、multiprocess模块、创建进程方式、join()、守护进程

    一.在python程序中的进程操作 之前已经了解了很多进程相关的理论知识,了解进程是什么应该不再困难了,运行中的程序就是一个进程.所有的进程都是通过它的父进程来创建的.因此,运行起来的python程序 ...

  7. python 进程 multiprocessing模块

    一.multiprocess.process模块 1.process类 Process([group [, target [, name [, args [, kwargs]]]]]),由该类实例化得 ...

  8. 守护进程,互斥锁, IPC ,Queue队列,生产消费着模型

    1.守护进程 什么是守护进程? 进程是一个正在运行的程序 守护进程也是一个普通进程,意思是一个进程可以守护另一个进程,比如如果b是a的守护进程,a是被守护的进程,如果a进程结束,b进程也会随之结束. ...

  9. Python 线程(threading) 进程(multiprocessing)

    *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !important; } /* ...

随机推荐

  1. 通过docker安装elasticsearch和安装ik分词器插件及安装kibana

    前提: 已经安装好docker运行环境: 步骤: 1.安装elasticsearch 6.2.2版本,目前最新版是7.2.0,这里之所以选择6.2.2是因为最新的SpringBoot2.1.6默认支持 ...

  2. Zero-shot Learning / One-shot Learning / Few-shot Learning

    Zero-shot Learning / One-shot Learning / Few-shot Learning Learning类型:Zero-shot Learning.One-shot Le ...

  3. git config命令详解

     Git有一个工具被称为git config,它允许你获得和设置配置变量:这些变量可以控制Git的外观和操作的各个方面. 一. 配置文件的存储位置 这些变量可以被存储在三个不同的位置: 1./etc/ ...

  4. NOIP2017[提高组] 宝藏 题解

    解析 我们观察范围可以发现n非常的小,(一般来说不是搜索就是状压dp)所以说对于这题我们可以用记忆化搜索或者dp,我们发现起点不同那么最终答案也就不同,也就是说答案是跟起点有关的,于是我们便可以想到去 ...

  5. Python04之数据类型

    Python的数据类型主要有四类:整型.浮点型.字符串类型.布尔类型 整型:所有整数都属于整型(长整型和整型)         如:-121,0,765,89,12306 浮点型:数字上有小数点的数 ...

  6. 别再裸奔了,你的项目代码安全吗,再不加密就out了

    在工作中,有时候我们需要部署自己的Python代码 或进行私有化部署时,尤其现在都是通过docker镜像部署,我们并不希望别人能够看到自己的Python源程序. 加密Python源代码的方式,是将.p ...

  7. 从零开始学Flask框架-005

    表单 Flask-WTF 项目结构 pip install flask-wtf 为了实现CSRF 保护,Flask-WTF 需要程序设置一个密钥.Flask-WTF 使用这个密钥生成加密令牌,再用令牌 ...

  8. ActiveMQ 消息队列服务

      1 ActiveMQ简介 1.1 ActiveMQ是什么 ActiveMQ是一个消息队列应用服务器(推送服务器).支持JMS规范. 1.1.1 JMS概述 全称:Java Message Serv ...

  9. react 提示消息队列 (支持动态添加,删除,多实例化)

    import React from 'react' import PropTypes from 'prop-types' import AnimationOperateFeedbackInfo fro ...

  10. P1777 帮助_NOI导刊2010提高(03)

    也许更好的阅读体验 \(\mathcal{Description}\) Bubu的书架乱成一团了!帮他一下吧! 他的书架上一共有n本书.我们定义混乱值是连续相同高度书本的段数.例如,如果书的高度是30 ...