Python进程、进程池以及进程间通信 怼烎@ 2022-06-03 00:11 337阅读 0赞 ## 概念 ## ### 进程 ### * 进程是系统进行资源分配和调度的基本单位,是操作系统结构的基础。简单可以理解为正在运行的程序的实例,都会有一个pid作为标识。 ### 多任务 ### * 以操作系统为例,在上面可以同时运行QQ word 微信等多个程序,这就是多任务 * 之前的单核CPU也可以运行多任务,让任务交替执行,因为CPU执行效率很快,所以对用户是无感知的 * 真正的多任务只能在多核CPU上完成,但如果任务数量大于CPU核心数量,操作系统也会自动把很多任务轮流调度到每个核心上执行 ## 创建进程 ## ### fork创建进程 ### #### fork创建子进程 #### * python中的os模块封装了常见的系统调用,其中包含fork,通过fork可以轻松的创建子进程 import os import time pid = os.fork() print('pid is %s' % pid) if pid < 0: print('fork调用失败') elif pid == 0: # while True: # print('this is 子进程') # time.sleep(1) print('this is child process(%s) and parent is (%s)' % (os.getpid(), os.getppid())) else: print('this is parent process(%s) and my child is (%s)' % (os.getpid(), pid)) print('父子进程都可以执行这里的代码') 输出结果为: pid is 6661 this is parent process(6660) and my child is (6661) 父子进程都可以执行这里的代码 pid is 0 this is child process(6661) and parent is (6660) 父子进程都可以执行这里的代码 * 在unix和Linux中,fork这个系统函数比较特殊,调用一次,返回两次。因为操作系统自动把当前进程复制一份,然后分别在父进程和子进程中返回 * 当程序执行到fork时,操作系统会创建一个新的进程(子进程),然后复制父进程的所有信息到子进程中 * 父进程和子进程都会从fork函数中得到一个返回值,在子进程中该值为0,父进程中则是子进程的ID #### 多进程修改全局变量 #### * 多进程中,每个进程的变量(包括全局变量)都维系一份,互不影响 import os import time rac = os.fork() num = 1 if rac == 0: time.sleep(1) num += 1 print('---parent num = %d' % num) else: num += 1 time.sleep(1) print('---child num = %d' % num) print('---end num = %d' % num) 输出结果为: ---child num = 2 ---end num = 2 ---parent num = 2 ---end num = 2 #### 多次fork #### * 每次fork都会创建一个子进程,并且后续代码每个进程都会执行到。 import os import time rac = os.fork() if rac == 0: print('---demo1---') else: print('---demo2---') rac2 = os.fork() if rac2 == 0: print('---demo3---') else: print('---demo4---') print('---end---') 输出结果为: ---demo2--- ---demo4--- ---end--- ---demo3--- ---end--- ---demo1--- ---demo4--- ---end--- ---demo3--- ---end--- * 如上所示,demo1 demo2各执行一次,demo3 demo4各执行两次,end累计执行四次 #### 父子进程执行顺序 #### * 执行顺序没有规律,取决于操作系统的调度算法 ### multiprocessing创建进程 ### > 上面提到的fork,只能在linux unix上调用,在Windows上是无法执行的,但python作为一个跨平台的语言,自然而然也提供了一个跨平台多进程的模块–multiprocessing #### 通过process创建进程 #### * Process代表一个进程对象 * 创建子进程时,只需要在构造函数内传入目标函数以及参数,调用start方法即可 * join()方法是让主进程等待子进程结束后再执行后续代码 import os from multiprocessing import Process from time import sleep def run_proc(name): print('子进程运行中,name = %s,pid = %d ...' % (name, os.getpid())) """ 创建子进程时,只需要传入一个执行函数和函数的参数,创建一个Process实例,用start方法启动 join方法可以等待子进程结束后再往下运行,通常用于进程间的同步 """ if __name__ == '__main__': print('父进程 %d.' % os.getpid()) p = Process(target=run_proc, args=('test',)) print('子进程将要执行') p.start() p.join() print('子进程执行完毕') 输出结果为: 父进程 6784. 子进程将要执行 子进程运行中,name = test,pid = 6785 ... 子进程执行完毕 #### 通过Process子类创建进程 #### * 创建进程还可以使用类的方式,自定义一个类,继承Process即可 import time from multiprocessing import Process class MyProcess(Process): def __init__(self, time, ): Process.__init__(self) self.time = time def run(self): # while True: print('---run---') # time.sleep(self.time) if __name__ == '__main__': p = MyProcess(1) p.start() p.join() print('--main end--') 输出结果为: ---run--- --main end-- ### 进程池Pool ### * 当创建的子进程数量不多时,可以通过Process来创建,如果要创建的子进程数量太多,那么就不适用这种方式了,需要使用进程池来解决。 * 初始化一个Pool时,可以指定一个最大进程数,如果超过这个数,那么请求就会等待,直到池中有进程结束,才会创建新的进程来执行 from multiprocessing import Pool import time, os, random def worker(msg): t_start = time.time() print('%s 开始执行,进城号为 %d' % (msg, os.getpid())) # random.random()随机生成0-1的浮点数 time.sleep(random.random() * 2) t_stop = time.time() print(msg, " 执行完毕,耗时%0.2f" % (t_stop - t_start)) # 创建一个进程池,里面最多三个进程 pool = Pool(3) for i in range(0, 10): # 向进程池中添加人物 # 如果人物的数量超过进程池大小的话,会等待进程池中有空闲的时候,自动添加进去 pool.apply_async(worker, args=(i,)) print('---start---') # 关闭进程池 关闭后po不再接受新的请求 pool.close() # 等待po中所有子进程执行完成,必须放在close语句之后 # 如果没有join,会导致进程中的任务不会执行 pool.join() print('---end---') 输出结果为 ---start--- 0 开始执行,进城号为 6804 1 开始执行,进城号为 6805 2 开始执行,进城号为 6803 0 执行完毕,耗时1.23 3 开始执行,进城号为 6804 2 执行完毕,耗时1.29 4 开始执行,进城号为 6803 1 执行完毕,耗时1.85 5 开始执行,进城号为 6805 4 执行完毕,耗时0.82 6 开始执行,进城号为 6803 3 执行完毕,耗时0.90 7 开始执行,进城号为 6804 6 执行完毕,耗时0.54 8 开始执行,进城号为 6803 5 执行完毕,耗时0.89 9 开始执行,进城号为 6805 7 执行完毕,耗时0.70 9 执行完毕,耗时0.92 8 执行完毕,耗时1.77 ---end--- * apply\_async 异步非阻塞 * apply 阻塞,上个进程结束才会执行下一个进程 * close 关闭pool 不再接收请求 * join 主进程阻塞,等待子进程运行结束,必须在closez以后 ### 进程间通信 ### #### 进程中的Queue #### * 进程间有时需要互相通信,操作系统提供了多种方式进行通信,Queue就是其中的一种 * queue就是一个消息队列,提供了get() put() qsize() empty() full()等方法对队列进行操作 * 下面以queue为例,创建两个进程,分别用来存储数据 from multiprocessing import Queue, Process import random, time # queue,实现多进程之间的数据传递,其实就是个消息队列 def write(q): for value in ['A', 'B', 'C']: print("put %s to queue" % value) q.put(value) time.sleep(random.random()) def read(q): while True: if not q.empty(): value = q.get() print("Get value is %s" % value) time.sleep(random.random()) else: break if '__main__' == __name__: q = Queue() qw = Process(target=write, args=(q,)) qr = Process(target=read, args=(q,)) qw.start() qw.join() qr.start() qr.join() print('---end---') print(random.randint(2, 4)) print(random.random()) 输出结果为: put A to queue put B to queue put C to queue Get value is A Get value is B Get value is C ---end--- 4 0.5260624608369131 #### 进程池中的Queue #### * 如果使用进程池pool创建进程的话,就需要使用Manager().Queue() from multiprocessing import Manager, Process, Pool import threading import random, time,os # queue,实现多进程之间的数据传递,其实就是个消息队列 def write(q): print('---write thread is %s' % os.getpid()) for value in ['A', 'B', 'C']: print("put %s to queue" % value) q.put(value) def read(q): print('---read thread is %s' % os.getpid()) for i in range(q.qsize()): print("Get value is %s" % q.get(True)) if '__main__' == __name__: print('---main thread is %s' % os.getpid()) q = Manager().Queue() po = Pool() po.apply(write, args=(q,)) po.apply(read, args=(q,)) po.close() po.join() print('---end---') 输出结果为: ---main thread is 6907 ---write thread is 6909 put A to queue put B to queue put C to queue ---read thread is 6910 Get value is A Get value is B Get value is C ---end--- ### 总结 ### * 本文主要介绍了进程概念 多任务 进程池 创建进程的几种方式以及进程间的通信等 * fork 不建议使用,不支持跨平台 * process * process函数形式 * 继承Process,实现子类 * pool * 进程池,当要创建的进程数量比较多时,建议使用该方式 * queue * 进程间通信 * 使用process创建的进程,使用Queue() * 使用进程池创建的进程通信,使用Manager().Queue()
相关 进程间通信 管道是Unix中最古老的进程间通信的形式。我们把从一个进程连接到另一个进程的一个数据流称为一个“管道“我们之前说进程间通信的本质是让不同的进程看到同一份资源,管道就是其中... 一时失言乱红尘/ 2024年04月25日 20:18/ 0 赞/ 93 阅读
相关 进程间通信 进程间通信(IPC,InterProcess Communication)是指在不同进程之间传播或交换信息。 IPC的方式通常有管道(包括无名管道和命名管道)、消息队列、信号 以你之姓@/ 2024年02月18日 20:08/ 0 赞/ 62 阅读
相关 进程间通信 进程间通信(IPC,Inter-Process Communication),是指两个或两个以上的进程之间传递数据或信号的一些技术或方法。进程是计算机系统分配资源的最小单位,每 ゞ 浴缸里的玫瑰/ 2023年01月02日 15:24/ 0 赞/ 168 阅读
相关 Python进程、进程池以及进程间通信 概念 进程 进程是系统进行资源分配和调度的基本单位,是操作系统结构的基础。简单可以理解为正在运行的程序的实例,都会有一个pid作为标识。 多任务 怼烎@/ 2022年06月03日 00:11/ 0 赞/ 338 阅读
相关 【进程(3)】—— 进程间通信 进程间通信 每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户 以你之姓@/ 2022年05月29日 02:40/ 0 赞/ 324 阅读
相关 进程间通信 进程间通信的基本概念 进程间通信意味着两个不同进程间可以交换数据,操作系统中应提供两个进程可以同时访问的内存空间。 通过管道实现进程间通信 基于管道(P 港控/mmm°/ 2022年05月25日 09:42/ 0 赞/ 364 阅读
相关 进程间通信 程序员必须让拥有依赖关系的进程集协调,这样才能达到进程的共同目标。可以使用两种技术来达到协调。第一种技术在具有通信依赖关系的两个进程间传递信息。这种技术称做进程间通信(inte 谁践踏了优雅/ 2022年01月16日 14:09/ 0 赞/ 378 阅读
相关 进程间通信 转载自:[http://songlee24.github.io/2015/04/21/linux-IPC/][http_songlee24.github.io_2015_04_ 不念不忘少年蓝@/ 2021年09月23日 04:10/ 0 赞/ 500 阅读
相关 进程间通信 进程间通信 1. 前言 2. 使用文件实现进程间的通信 3. 使用管道实现进程间的通信 4. 共享内存 5. 以上三种通信方式的区别 青旅半醒/ 2021年08月30日 22:05/ 0 赞/ 524 阅读
还没有评论,来说两句吧...