什么是多进程-多线程-多协程 ----进程和多进程
进程和多进程
- 进程和多进程
- 进程
- 概念:
- 组成:
- 基本状态:
- 创建:
- 如何创建子进程?
- OS.fork 创建子进程
- 返回值
- os.getpid():获取进程的进程号
- os.getppid():获取父进程的进程号
- 孤儿进程
- 僵尸进程
- multiprocessing.Process
- 创建管理进程模块:
- Process类
- 多进程 -- 数据共享
- 使用multiprocessing.Array共享数据
- 使用multiprocessing.Manager共享数据
- 使用multiprocessing.Queue共享数据
- multiprocessing.Pool
- 多进程资源消耗
- Pool 进程池
- 进程池的原理:
- Pool 主进程管理进程的机制:
- Pool 进程池的应用场景
- Pool 类
- Python中多进程与多线程
- 多进程与多线程的选择
进程和多进程
进程
概念:
进程(Process)是计算机中的程序关于某数据 集合上的一次运行活动,是系统进行资源分配的基本单 位。
多个进程同时执行时,每个进程的执行都需要由操作系 统按一定的算法(RR调度、优先数调度算法等)分配 内存空间。
组成:
进程控制块PCB、数据段、正文段
基本状态:
就绪状态、运行状态和阻塞状
创建:
用户创建出来的所有进程都是由操作系统负责,新进程的创建都是由一个已经存在的进程执 行了一个用于创建进程的系统调用而创建的 Linux中pid为0的进程,是所有进程的主进程
如何创建子进程?
在python中,每一个运行的程序都有一个主进程,可以利用模块中封装的方法来创建子进程 (os.fork =>linux、multiprocessing)
OS.fork 创建子进程
os.fork就是用来创建子进程的方法,os.fork中就用来创建子进程的方法
- 使用fork创建子进程后,操作系统会将当前的进程复制一份
- 原来的进程称为父进程,新创建的进程称为子进程
- 两个进程会各自互不干扰的执行下面的程序
- 父进程与子进程的执行顺序与系统调度有关
- 在子进程内,这个方法会返回0;在父进程内,这个方法会返回子进程的编号PID
返回值
- 返回值为大于0时,此进程为父进程,且返回的数字为子进程的PID;
- 当返回值为0时,此进程为子进程
- 如果返回值为负数则表明创建子进程失败
- 父进程结束时,子进程并不会随父进程立刻结束。同样,父进程不会等待子进程执行完
os.getpid():获取进程的进程号
os.getppid():获取父进程的进程号
import os,timeprint("start...")pid = os.fork()
#父进程运行时得到的pid为子进程的pid,子进程运行时这个pid就是0
print("outerside pid is:",pid)
if pid == 0:print("child process")time.sleep(60)print("child pid is:",os.getpid())print("child-parent pid is:",os.getpid())
else:print("parent process")time.sleep(60)print("parent pid is:",os.getpid())
孤儿进程
父进程退出,子进程还在运行,那么这个子进程就会成为孤儿进程,孤儿进程会被怕pid为1 的进程收养
[root@Hardy 8_10]# ps -ef|grep python3
root 1679 1 0 15:12 pts/0 00:00:00 python3 process1.py
root 1685 1545 0 15:13 pts/1 00:00:00 grep --color=auto python3
僵尸进程
子进程退出,父进程没有响应。父进程没有调用wait()或者waitpid去获取子进程的状态,子进程的进程控制块就会依然保存在系统中国,这种进程就称之为僵尸进程
[root@Hardy 8_10]# ps aux|grep python3
root 1663 0.0 0.3 124932 5692 pts/0 S+ 15:06 0:00 python process1.py
root 1664 0.0 0.0 0 0 pts/0 Z+ 15:06 0:00 [python3] <defunct>
root 1666 0.0 0.0 112824 976 pts/1 S+ 15:06 0:00 grep --color=auto python3
multiprocessing.Process
Multiprocessing 由于windows没有fork调用,python提供了multiprocessing支持跨平台版本。
创建管理进程模块:
- Process(用于创建进程)
- Pool(用于创建管理进程池)
- Queue(用于进程通信,资源共享)
- Value,Array(用于进程通信,资源共享)
- Pipe(用于管道通信)
- Manager(用于资源共享)
Process类
构造方法:Process([group [, target [, name [, args [, kwargs]]]]])
- group: 线程组,目前还没有实现,库引用中提示必须是None;
- target: 要执行的方法;
- name: 进程名;
- args/kwargs: 要传入方法的参数
实例方法:
- p.start():启动进程,并调用该子进程中的p.run()
- p.run(): strat()调用run方法,如果实例进程时未制定传入target,这star执行t默认run()方法
- p.terminate(): 不管任务是否完成,立即停止工作进程
- p.is_alive(): 如果p仍然运行,返回True
- p.join([timeout]): 阻塞当前上下文环境的进程,直到调用此方法的进程终止或到达指定的timeout
import multiprocessing
from multiprocessing import Process,current_process
import timelst = []
def task(i):print(current_process().name,i,'start...')time.sleep(2)lst.append(i)print(lst)print(current_process().name,i,'end....')if __name__ == "__main__":p_lst = []for i in range(4):p = Process(target = task,args = (i,)) #只能在程序入口创建多进程p_lst.append(p)p.start()print("st")for p in p_lst:p.join()print("main end....")# class Myprocess(multiprocessing.Process):
# def __init__(self,num):
# super(Myprocess,self).__init__()
# self.num = num
# def run(self):
# print(current_process().name)
# print(f"running on process:{self.num}")
#
# if __name__ == "__main__":
# t1 = Myprocess(1)
# t2 = Myprocess(2)
# t1.start()
# t2.start()
多进程 – 数据共享
不同进程间内存是不共享的,multiprocessing中提供以下方式实现进程间的数据交换
- Queue(用于进程通信,资源共享)
- Value,Array(用于进程通信,资源共享)
- Pipe(用于管道通信)
- Manager(用于资源共享)
使用multiprocessing.Array共享数据
- 创建Array时,需要指定数据类型
- 如:arr = Array(‘i’ , [11, 22, 33, 44])
- 'i’表示数据类型:“d”表示一个双精度的浮点数, “i”表示一个有符号的整数
使用multiprocessing.Manager共享数据
- 由Manager()返回的manager提供 list, dict, Namespace, Lock, RLock, Semaphore,
- BoundedSemaphore, Condition, Event, Barrier, Queue, Value and Array类型的支持。
- Manager比Array要好用一点,因为它可以同时保存多种类型的数据格式
from multiprocessing import Manager,Process,Lock
import time
def func(i,temp):with lock:time.sleep(1)temp[0] += 100# time.sleep(1)print(i,"-------->",temp[0])#使用manager 父进程要等待子进程结束再退出
lock = Lock()
if __name__ == "__main__":manager = Manager()temp = manager.list([1,2,3])p_list = []for i in range(10):p = Process(target=func,args=(i,temp))p.start()p_list.append(p)for i in p_list:i.join() #不加join,manager进程会先退出,子进程就访问不到manager共享的数据了
使用multiprocessing.Queue共享数据
- 消息队列:multiprocessing.Queue
Queue是对进程安全的队列,可以使用Queue实现对进程之间的数据传输;还有一个重要作用是作
为缓存使用。
Queue(maxzize = 0) 创建一个队列对象,maxsize 表示队列中最多存放消息的数量。 - 实例方法:
- put(obj [, block=True[, timeout]]):调用队列对象的put()方法将obj插入到队列中
- get([block=True[, timeout]]):get方法可以将队列中读取并删除一个元素
- full():判断队列是否为满
- empty():判断队列是否为空
- qsize():获取队列中消息数量
Queue不能再Pool进程池中使用,使用Multiprocessing.Manager类可以适用Pool类 - 不一致读
- 为了防止和多线程一样的出现数据抢夺和脏数据的问题,同样需要设置进程锁。与threading类 似,在multiprocessing里也有同名的锁类RLock, Lock, Event, Condition, Semaphore,连用法 都是一样样的!
- 当创建进程时(非使用时),共享数据会被拿到子进程中,当进程中执行完毕后,再赋值给原值
#队列
from multiprocessing import Process,Queue
import time
def func(i,q):if not q.empty():print(i,"----->get value,",q.get())time.sleep(2)
#先进先出
if __name__ == "__main__":q = Queue() #不能用在进程池for i in range(6):q.put(10-i)p = Process(target=func,args=(i,q))p.start()
multiprocessing.Pool
多进程资源消耗
一般我们是通过动态创建子进程(或子线程)来实现并发服务器的,但是会存在这样一些缺点:
- 动态创建进程(或线程)比较耗费时间,这将导致较慢的服务器响应。
- 动态创建的子进程通常只用来为一个客户服务,这样导致了系统上产生大量的细微进程(或
线程)。进程和线程间的切换将消耗大量CPU时间。 - 动态创建的子进程是当前进程的完整映像,当前进程必须谨慎的管理其分配的文件描述符和
堆内存等系统资源,否则子进程可能复制这些资源,从而使系统的可用资源急剧下降,进而
影响服务器的性能。
所以呢,就引入了进程池的概念
Pool 进程池
- 进程池的作用:有效的降低频繁创建销毁线程所带来的额外开销。
进程池的原理:
- 进程池都是采用预创建的技术,在应用启动之初便预先创建一定数目的进程。
- 应用在运行的过程中,需要时可以从这些进程所组成的进程池里申请分配一个空闲的进程,来执
行一定的任务,任务完成后,并不是将进程销毁,而是将它返还给进程池,由线程池自行管理。 - 如果进程池中预先分配的线程已经全部分配完毕,但此时又有新的任务请求,则进程池会动态的
创建新的进程去适应这个请求。 - 某些时段应用并不需要执行很多的任务,导致了进程池中的线程大多处于空闲的状态,为了节省
系统资源,进程池就需要动态的销毁其中的一部分空闲进程。 - 进程需要一个管理者,按照一定的要求去动态的维护其中进程的数目
Pool 主进程管理进程的机制:
- 最简单、最常用的算法是随机算法和Round Robin(轮流算法)
- 主进程和所有子进程通过一个共享的工作队列来实现同步:子进程都睡眠在该工作队列上,当有
新的任务到来时,主进程将任务添加到工作队列中。这将唤醒正在等待任务的子进程,不过只有一个子进程将获得新任务的“接管权”,它可以从工作队列中取出任务并执行之,而其他子进程将继续睡眠在工作队列上。 - 当选择好子进程后,主线程程还需要使用某种通知机制来告诉目标子进程有新任务需要处理,并传递必要的数据。我们可以把这些数据定义为全局,那么它们本身就是被所有进程共享的。对于进程池而言,最简单的方式是,在父进程和子进程之间预先建立好一条管道,然后通过管道来实现所有的进程间通信
Pool 进程池的应用场景
- 需要大量的进程来完成任务,且完成任务的时间比较短。
- 但对于长时间的任务,比如一个Telnet连接请求,进程池的优点就不明显了。因为Telnet会话时
间比线程的创建时间大多了
Pool 类
构造方法
- Pool([processes[, initializer[, initargs[, maxtasksperchild[, context]]]]])
- processes :使用的工作进程的数量,如果processes是None那么使用 os.cpu_count()返回的数量。
- initializer: 如果initializer是None,那么每一个工作进程在开始的时候会调用initializer(*initargs)。
- maxtasksperchild:工作进程退出之前可以完成的任务数,完成后用一个新的工作进程来替代原进程,来让闲置的资源被释放maxtasksperchild默认是None,意味着只要Pool存在工作进程就会一直存活。
实例方法
- apply_async(func[, args[, kwds[, callback]]]) 它是非阻塞。
- apply(func[, args[, kwds]])是阻塞的。
- close() 关闭pool,使其不在接受新的任务。
- terminate() 关闭pool,结束工作进程,不在处理未完成的任务。
- join() 主进程阻塞,等待子进程的退出, join方法要在close或terminate之后使用。这样是因为被终止的进程需要被父进程调用wait(join等价与wait),否则进程会成为僵尸进程
注意:
① 使用Pool创建进程池对象,同时进程池中进程已经启动
② 向进程池对象中添加事件,事件排队执行
③ 如果主进程退出,则进程池中所有进程都退出
from multiprocessing import Pool,current_process
import time
lst = []
def task(i):print(current_process().name,i,'start...')time.sleep(2)lst.append(i)print(lst)print(current_process().name,i,'end....')if __name__ == "__main__":#创建一个进程池,建议进程数和cpu核数一致即可,也可也等于alter,cpu几核就几个#maxtasksperchild=3,每个work进程最多只能处理三个任务,执行完就挂了#释放内存空间p = Pool(processes=4,maxtasksperchild=3)# time.sleep(100)for i in range(20):#进程池接受任务p.apply_async(func=task,args=(i,))#关闭进程池,不接受新任务,已经创建的任务会继续运行p.close()#等待子进程执行完毕,父进程再执行p.join()print("end...........")
Python中多进程与多线程
多进程与多线程的选择
- io密集型计算用多线程
- cpu密集型计算用多进程
什么是多进程-多线程-多协程 ----进程和多进程相关推荐
- 什么是多进程-多线程-多协程 ----进程和多线程
进程和多线程 进程通信 进程的组成 进程之间的通信方式 管道 匿名管道 命名管道 信号 信号量 共享内存 socket 消息队列 进程线程 系统知识 cpu时间片(抽象概念) 线程: 进程: 进程与线 ...
- 基于Python3多进程(多线程)+多协程的数据并发处理模版
图片来源:elenabsl/Shutterstock 上一篇文章<基于Python3单进程+多线程+多协程的生产者-消费者模型示例代码>介绍了如何使用Python在单进程的情况下利用协程并 ...
- python 多线程和协程结合_一文讲透 “进程、线程、协程”
本文从操作系统原理出发结合代码实践讲解了以下内容: 什么是进程,线程和协程? 它们之间的关系是什么? 为什么说Python中的多线程是伪多线程? 不同的应用场景该如何选择技术方案? ... 什么是进程 ...
- python进程线程协程区别_Python3多线程与协程
python中的多线程非常的常用,之前一直糊里糊涂地使用,没有一些系统性的概念,记录一下~ 0x001 多线程的优势:可将长时间占用的程序放到后台 可能会加速程序执行速度 能够实现一些类似同步执行的效 ...
- php协程 多线程,【swoole.2.01】多进程,多线程和协程
前言 在体验swoole的协程功能之前,需要先知道多进程,多线程和协程的区别. 多进程 典型的多进程结构就是耳熟能详的master-worker结构.swoole本身也是由master-maneger ...
- python多线程好还是多协程好_深入浅析python中的多进程、多线程、协程
进程与线程的历史 我们都知道计算机是由硬件和软件组成的.硬件中的CPU是计算机的核心,它承担计算机的所有任务. 操作系统是运行在硬件之上的软件,是计算机的管理者,它负责资源的管理和分配.任务的调度. ...
- gunicorn多进程不死_WEB,gunicorn - 无论是多进程、多线程、协程模式,同一个浏览器窗口多个标签页访问同一个url,看上去不会并发的问题...
TL;DR 其实是浏览器同一个窗口下限制了对同一个url会执行串行操作. 1.参考 2.现象 我有一个WSGI APP,每次处理request都睡眠5秒.不管多进程.多线程.协程跑WSGI APP,同 ...
- 【2021最新版】Python 并发编程实战,用多线程、多进程、多协程加速程序运行
[2021最新版]Python 并发编程实战,用多线程.多进程.多协程加速程序运行 视频地址:https://www.bilibili.com/video/BV1bK411A7tV/ P1 Pytho ...
- 使用单线程,多线程,协程,异步爬取包图网大国工匠视频
使用单线程爬取,多线程,协程爬取,异步爬取包图网视频 文章目录 使用单线程爬取,多线程,协程爬取,异步爬取包图网视频 第一步使用单线程爬取包图网视频 多线程爬取视频 用协程爬取 基于协程的异步爬取 第 ...
最新文章
- [luoguP2618] 数字工程(DP)
- 网络时间服务和chrony
- js离焦事件_JavaScript中的事件
- 【温故知新】CSS学习笔记(背景)
- PHP表单提交参数验证类(可修改)
- conda创建环境及激活环境失败问题
- 程序员春节回家装逼指南
- 学习笔记:人工势场法
- 里氏代换原则(企鹅是不是鸟!)
- 保护信息安全,实名认证该怎么选?
- 符号在excel中的引用_如何在Excel中添加项目符号
- python爬虫:淘宝图片爬虫
- 冯诺依曼计算机主机,冯诺依曼结构计算机包括哪五大部分?
- unity中的动态绑定、赋值等
- python php c#爬虫_php 爬虫采集
- Android实战-忘记密码案例
- 乌克兰国防部等网站再次遭受网络攻击,如何应对频发的网络安全事件
- 【一步教学,一步到位,移动手机app开发
- 元素周期表排列的规律_元素周期表元素口诀和排列规律(5)
- 青村茶舍||“麦穗飘香”文旅推介直播活动