进程:程序正在执行的过程,就是一个正在执行的任务,而负责执行任务的就是cpu
操作系统:操作系统就是一个协调、管理和控制计算机硬件资源和软件资源的控制程序。
操作系统的作用:
1:隐藏丑陋复杂的硬件接口,提供良好的抽象接口
2:管理、调度进程,并且将多个进程对硬件的竞争变得有序。
多道技术产生的背景:针对单核,实现并发。
多道技术:多道技术中的多道指的是多个程序,多道技术的实现是为了解决多个程序竞争或者说共享同一个资源(比如cpu)的有序调度问题,解决方式即多路复用,多路复用分为时间上的复用和空间上的复用。,
时间复用:当一个程序在等待I/O时,另一个程序可以使用cpu。提高CPU的利用率。
空间复用:同一时间内存可以缓存更多的程序。
进程
主进程和子进程的关系
关于资源:子进程得到的是除了代码段是与父进程共享的意外,其他所有的都是得到父进程的一个副本,子进程的所有资源都继承父进程,得到父进程资源的副本,既然为副本,也就是说,二者并不共享地址空间。,两个是单独的进程,继承了以后二者就没有什么关联了,子进程单独运行。(采用写时复制技术)
关于文件描述符:继承父进程的文件描述符时,相当于调用了dup函数,父子进程共享文件表项,即共同操作同一个文件,一个进程修改了文件,另一个进程也知道此文件被修改了。
子进程回收机制:进程由谁开,由谁回收,等子程序完成后,才由父进程回收
创建进程的方法
方法一:利用subprocess 模块,具体方法见网络编程模块。subprocess模块有很大的局限性。) 我们总是让subprocess运行外部的程序,而不是运行一个Python脚本内部编写的函数。2) 进程间只通过管道进行文本交流。以上限制了我们将subprocess包应用到更广泛的多进程任务。
方法二:利用multiprocessing模块,这个模块叫多进行模块管理包,multiprocessing中的Process中的类可以创建一个子进程。
multiprocessing.Process(group=None, target=None, name=None, args=(), kwargs={})
一般需要指定的参数为target,表示为一个函数,这个函数必须是可调用的,
args表示为target的参数,是一个元组的格式。注意在只有一个参数的时候,必须写上逗号,表示为一个tuple
kwargs表示为target的参数,是一个字典的格式。
注意:在Windows系统中开子进程的代码必须要写在if __name__="__main__" 下。mac笔记本不需要因为这两种系统开启子进程的方式不一样
范例
from multiprocessing import Process
import time
def task(name):
print("%s is runing"%name)
time.sleep(2)
print("%s is done"%name)
if __name__=="__main__":
p=Process(target=task,args=("egon",))
p.start()#只是给操作系统发了个信号,让操作系统开进程(同时开辟一块内存空间,拷贝父进程的地址空间)
print("你好,中国")
结果:
你好,中国 egon is runing egon is done
子进程回收机制:进程由谁开,由谁回收,等子程序完成后,才由父进程回收
p.start()#只是给操作系统发了个信号,让操作系统开一个进程(同时开辟一块内存空间,拷贝父进程的地址空间),同时我们还要知道start方法是一个异步非阻塞,
因为当执行start方法的时候,并不会阻塞当前进程去等待子进程执行完,而是会继续执行主进程的代码 p.join() :#阻塞当前进程,直到调用join方法的那个进程执行完,再继续执行当前进程。也就是说主进程等子进程完了,自己再进行进程。join()方法是一个同步阻塞 p.pid #子进程id p.terminate() #强制结束进程,这个方法不能自己杀死自己 p.is_alive() #查看该进程是否活着 # 进程自杀 import os import signal os.kill(os.getpid(), signal.SIGKILL)
python代码由python解释器解释运行,你启用的python进程其实是运用了python exe的进行。
os .getppid()查看父进程的ID
os.getpid()获取当前的进程的ID。
方法三:
from multiprocessing import Process class MyProcess(Process): def __init__(self, n): self.n = n super(MyProcess, self).__init__() # 使用父类的方法才能传参 def run(self): """一定要有这个函数""" print(self.n) print(os.getppid(), os.getpid()) print("主进程", os.getppid(), os.getpid()) my = MyProcess("小红") my.start()
结果:
主进程 241 6452
小红
6452 6454
进程必须要知道的事情
1.进程之间内存相互隔离
举例
from multiprocessing import Process
n=1
def f():
global n
n=13
if __name__ == '__main__':
p =Process(target=f)
p.start()
p.join()
print(n)
结果:
1
结论:子进程不能修改主进程的变量,用global的作用就是看是否会修改全局变量,因为在单进程中可以修改。
2.子进程用主进程的值来作为自己的初始值。
n=1 def f(): print(n)
if __name__ == '__main__':
p =Process(target=f)
p.start()
结果:
1 1
3.不能获得子进程的返回值,因为进程之间是隔离的
创建并发进程服务器
进程之间的通信(IPC)
由于我们知道进程之间内存是隔离的,但是可以通过别的方法实现进程间的通信
大体有两个方面:
- 基于文件 适合于同一台机器上多进程进行通信.基于socket的文件级别的通信
- 基于网络 消息中间件(redis,rabbitmq,memcach)
- 数据安全.因为管道加锁
- 可以实现进程之间的通信
- 先进先出
实现原理:管道+加锁
第一: 队列
在进程中引用队列的方式:
from multiprocessing import Queue
在线程中引用队列的方式:
import queue
put:放数据,Queue.put( )默认有block=True和timeout两个参数。当block=True时,写入是阻塞式的,阻塞时间由timeout确定。当队列q被(其他线程)写满后,这段代码就会阻塞,直至其他线程取走数据。Queue.put()方法加上 block=False 的参数,即可解决这个隐蔽的问题。但要注意,非阻塞方式写队列,当队列满时会抛出 exception Queue.Full 的异常
get:取数据(默认阻塞),Queue.get([block[, timeout]])获取队列,timeout等待时间
————————————————
版权声明:本文为CSDN博主「brucewong0516」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/brucewong0516/article/details/85796073
举例子:
from multiprocessing import Process,Queue import os,time,random def write(q): """ 一个进程往队列中放东西 :param q: :return: """ for value in ["A","B","C"]: print('put %s to queue...'%value) q.put(value)#往里面放东西 time.sleep(random.random()) def read(q): """ 另一个进程从队列中拿数据 :param q: :return: """ while True: if not q.empty(): value=q.get(True)#从里面拿东西 print('GET %s from queue'%value) time.sleep(random.random()) else: break if __name__ == '__main__': q=Queue()#可以不设置大小,受限于内存的大小 pw=Process(target=write,args=(q,)) pr=Process(target=read,args=(q,)) pw.start() pw.join() pr.start() pr.join() print('所有的数据都拿完了')
结果:
put A to queue... put B to queue... put C to queue... GET A from queue GET B from queue GET C from queue 所有的数据都完了