代码创建进程
一、创建进程多种方式
# 进程就是运行中的程序 而怎样创建进程呢? ''' 1.双击点击桌面图标即可创建进程 2.也可以通过代码创建进程 python创建进程需要掌握两种 ''' # 如果想要通过代码创建进程需要模块 from multiprocessing import Process import time def task(name): print(f'{name}正在运行') time.sleep(3) print(f'{name}运行结束') if __name__ == '__main__': task('jason') # 普通的函数调用是同步操作 print('主进程') ''' 如果是 普通的函数调用是同步操作 jason正在运行 jason运行结束 主进程 '''
1.第一种
from multiprocessing import Process import time def task(name): print(f'{name}正在运行') time.sleep(3) print(f'{name}运行结束') if __name__ == '__main__': p = Process(target=task, args=('jason',)) # 创建一个进程对象 p.start() # 告诉操作系统创建一个进程(异步操作 print('主进程') ''' 运行结果: 主进程 jason正在运行 jason运行结束 首先在内存中会开一个内存空间给这个py文件执行代码 p = Process(target=task, args=('jason',)) 就是创建一个进程对象 然后一行一行代码执行下来的时候遇到p.start() 就会告诉操作系统创建一个进程 然后操作系统就会在开一个内存空间把这个py文件的代码在复制一份 放到新开的内存空间执行 除了脚本文件没复制 但是p.start是异步操作所以提交请求完之后就会继续执行接下来的代码 所以会先打印 主进程 然后在执行task函数 ''' """ 创建进程的代码在不同的操作系统中 底层原理有区别!!! 在windows中 创建进程类似于导入模块 if __name__ == '__main__': 启动脚本 在mac、linux中 创建进程类似于直接拷贝 不需要启动脚本 但是为了兼容性 也可以使用 """
2.第二种
# 因为Process相当于一个类 所以我们可以自己写一个类然后在继承这个类 class MyProcess(Process): def __init__(self, name): super().__init__() self.name = name def run(self): print(f'{self.name}正在运行') time.sleep(3) print(f'{self.name}运行结束') if __name__ == '__main__': obj = MyProcess('jason') obj.start() print('主进程') ''' 运行结果: 主进程 jason正在运行 jason运行结束 执行过程跟第一种差不多 '''
二、join方法
1.推导过程
# 现在这么让子进程结束之后再让主进程执行代码 相当于让p.start()变成同步操作 1.我们可以直接在主进程中加入time.sleep()让主进程睡眠时间比子进程多 from multiprocessing import Process import time def task(name): print(f'{name}正在运行') time.sleep(3) print(f'{name}运行结束') if __name__ == '__main__': p = Process(target=task, args=('jason',)) # 创建一个进程对象 p.start() # 告诉操作系统创建一个进程(异步操作 time.sleep(5) print('主进程') ''' 运行结果: jason正在运行 jason运行结束 主进程 虽然这样可以但是之后我们如果不知道子进程需要运行的时间是多少怎么办? '''
2.推导过程
# 我们可以使用join方法 from multiprocessing import Process import time def task(name): print(f'{name}正在运行') time.sleep(3) print(f'{name}运行结束') if __name__ == '__main__': p = Process(target=task, args=('jason',)) # 创建一个进程对象 p.start() # 告诉操作系统创建一个进程(异步操作 p.join() print('主进程') ''' 运行结果: jason正在运行 jason运行结束 主进程 使用join方法可以完美解决这个问题 不管子进程运行时间多长join都等你运行完 才会运行主进程 '''
3.join用法
# 1. from multiprocessing import Process import time def task(name, n): print(f'{name}正在运行') time.sleep(n) print(f'{name}运行结束') if __name__ == '__main__': p1 = Process(target=task, args=('jason', 1)) # args就是通过元组的形式给函数传参 p2 = Process(target=task, args=('kevin', 2)) # 也可以通过kwargs={'name':'jason', 'n':1} 太麻烦 没必要 p3 = Process(target=task, args=('jerry', 3)) start_time = time.time() p1.start() # 因为p1.start默认是异步操作 所以p1让操作系统创建进程完的时候就会直接执行下面的代码 p2.start() # 然后p2.start p3.start都是也是一样的 所以当p1执行的时候p2和p3已经在执行了 p3.start() # 所以这个总耗时肯定就是某个进程运行的最长时间 那么肯定就是3秒左右 p1.join() p2.join() p3.join() end_time = time.time() - start_time print('总耗时:%s' % end_time) print('主进程') ''' 运行结果: jason正在运行 jerry正在运行 kevin正在运行 jason运行结束 kevin运行结束 jerry运行结束 总耗时:3.1356141567230225 主进程 ''' # 2. from multiprocessing import Process import time def task(name, n): print(f'{name}正在运行') time.sleep(n) print(f'{name}运行结束') if __name__ == '__main__': p1 = Process(target=task, args=('jason', 1)) # args就是通过元组的形式给函数传参 p2 = Process(target=task, args=('kevin', 2)) # 也可以通过kwargs={'name':'jason', 'n':1} 太麻烦 没必要 p3 = Process(target=task, args=('jerry', 3)) start_time = time.time() p1.start() p1.join() # 因为join方法是等待子进程结束才会执行主进程 所以当p1执行完的时候其他两个进程还没创出来呢 p2.start() # 所以p1结束的时候才会创建p2进程 并执行p2 p2.join() # 然后join会等待join执行结束然后在执行主进程的代码 p3.start() # 所以总耗时肯定是三个进程的运行时间相加 6秒左右 p3.join() end_time = time.time() - start_time print('总耗时:%s' % end_time) print('主进程') ''' 运行结果: jason正在运行 jason运行结束 kevin正在运行 kevin运行结束 jerry正在运行 jerry运行结束 总耗时:6.289183855056763 主进程 一定要看准join的执行位置 以及多任务情况下等待的目标 '''
三、进程间数据默认隔离
from multiprocessing import Process money = 100 def task(): global money money = 666 print('子进程打印的money', money) if __name__ == '__main__': p = Process(target=task) p.start() p.join() print('父进程打印的money', money) ''' 运行结果: 子进程打印的money 666 父进程打印的money 100 因为当p.start创建一个进程时会把除了脚本文件之外的代码复制到另一个内存空间执行 所以子进程执行代码的时候因为通过global修改money的名称空间 所以money的值被修改为了666 而主进程根本就没有调用task函数 所以money的值还是100 经此验证: 所以多个进程的数据默认是彼此隔离的 ''' """ 而想要让两个进程之间数据交互 那么必须要借组与 '管道' 和 '消息队列' """
四、ICP机制
1.预备知识
# 预备知识 # 什么是队列: 就是存数据先进先出 from multiprocessing import Queue # 1.创建一个队列对象 q = Queue(3) # 括号内指定队列中可以容纳多少个数据 不写默认:2147483647 # 2.添加数据 q.put(111) # print(q.full()) # False 判断队列数据是否存放满了 因为只放了一个111所以快点给是False q.put(222) q.put(333) # print(q.full()) # True 因为创建对象时我只可以容纳三个数据 所以我放了三个数据说明存满了 # q.put(444) # 如果数据存放超出极限 那么程序就会进入阻塞态 直到数据被取出 # 3.取数据 # print(q.get()) # 111 # print(q.empty()) # False 判断队列中的数据是否取完了 因为只取了一个111 还有两个数据还没有取走 所以快点给时False # print(q.get()) # 222 # print(q.get()) # 333 每次取一个值 现存先出 # print(q.empty()) # True 因为只有三个数据都被取走了 所以肯定是True # print(q.get()) # 如果取数据超出极限 那么程序机会进入阻塞态 直到存入数据 # 4.get_nowait print(q.get_nowait()) # 111 print(q.get_nowait()) # 222 print(q.get_nowait()) # 333 print(q.get_nowait()) # get_nowait是如果取出数据超出极限没有数据可取的时候 就会直接报错 ''' 上述三种方法: full() empty() get_nowait() 在多进程下无法精确使用(失效)!!! 因为如果是多进程的话 是都可以在消息队列中存数据和取数据的 如果一个进程使用full判断队列是否数据满了返回了一个满了 但是下一秒另一个进程取走了一个数据 那么队列肯定是不满的 所以会不准确 '''
2.ICP机制简介
''' IPC机制 1.主进程与子进程通信 2.子进程与子进程通信 ''' from multiprocessing import Process, Queue def procedure(q): q.put('子进程procedure往队里中添加了数据') # 这个函数是往q队列添加数据 def consumer(q): print('子进程的consumer从队列中获取数据', q.get()) # 这个函数是往q队列取数据 if __name__ == '__main__': q = Queue() # 在主进程中产生q对象 确保所有的子进程使用的是相同的q p1 = Process(target=procedure, args=(q,)) p2 = Process(target=consumer, args=(q,)) # 创建两个进程对象 p1 p2 p1.start() p2.start() print('主进程') # 这样就可以知道两个进程的数据是可以通过队列交互的 两个进程都可以往队列中存数据和取数据
五、生产者消费者模型
# 生产者: 就是生产数据 # 消费者: 处理数据 """ 回忆之前: 我们爬取网站的时候 生产者: 就是获取网页的数据(函数) 爬 消费者: 就是从网页中帅选出古河条件的数据(函数) 帅选 而完整的生产者消费者模型至少需要三个部分 生产者 消息队列/数据库 消费者 """
六、进程相关方法
# 1.查看进程号 from multiprocessing import current_process import os current_process().pid print(os.getpid()) os.getppid() ''' 进程号相当于端口号 不过进程号比端口号更细 端口号是给一个程序使用的 而进程号相当于是给进程使用的 程序下可以有多个进程 ''' # 2.销毁子进程 # p1.terminate() 就是把子进程给删了
# 3.判断进程是否存活 # p1.is_alive() 判断子进程是否删除了
1 os.getpid()
from multiprocessing import Process import time import os def task(name): print(f'{name}正在运行') time.sleep(3) print(f'{name}运行结束') print(f'子进程的进程号:{os.getpid()}') if __name__ == '__main__': p = Process(target=task, args=('jason',)) p.start() print(f'主进程的进程号:{os.getpid()}') print('主进程') # 两个进程的进程是不一样的 """ 主进程的进程号:6416 主进程 jason正在运行 jason运行结束 子进程的进程号:1164 """
2 os.getppid()
from multiprocessing import Process import time import os def task(name): print(f'{name}正在运行') time.sleep(3) print(f'{name}运行结束') print(f'task进程的父进程号:{os.getppid()}') if __name__ == '__main__': p = Process(target=task, args=('jason',)) p.start() print(f'主进程的进程号:{os.getpid()}') print('主进程') # 两个进程的进程是不一样的 """ os.getppid是查找该进程的父进程的进程号是多少 主进程的进程号:3496 主进程 jason正在运行 jason运行结束 task进程的父进程号:3496 """
3 current_process
from multiprocessing import current_process print(current_process().pid) # 1680 # current_process 也是查看该进程的进程号
4 p.terminate()
from multiprocessing import Process import time import os def task(name): print(f'{name}正在运行') time.sleep(3) print(f'{name}运行结束') print(f'task进程的父进程号:{os.getppid()}') if __name__ == '__main__': p = Process(target=task, args=('jason',)) p.start() p.terminate() # 就是把子进程给销毁了 print(f'主进程的进程号:{os.getpid()}') print('主进程') ''' 运行结果: 主进程的进程号:21824 主进程 '''
5 p.is_alive()
from multiprocessing import Process import time import os def task(name): print(f'{name}正在运行') time.sleep(3) print(f'{name}运行结束') print(f'task进程的父进程号:{os.getppid()}') if __name__ == '__main__': p = Process(target=task, args=('jason',)) p.start() p.terminate() print(p.is_alive()) # True 就是判断进程是否存活 print(f'主进程的进程号:{os.getpid()}') print('主进程') ''' 运行结果: True 主进程的进程号:10460 主进程 为什么我们使用terminate销毁了子进程 而is_alive 因为cpu处理速度执行太快了当你在销毁的时候 还没有销毁完 is_alive已经在执行判断进程有没有存活了 所以是True 只要让主进程休息一点时间就可以了 ''' from multiprocessing import Process import time import os def task(name): print(f'{name}正在运行') time.sleep(3) print(f'{name}运行结束') print(f'task进程的父进程号:{os.getppid()}') if __name__ == '__main__': p = Process(target=task, args=('jason',)) p.start() p.terminate() time.sleep(0.1) # 休息0.1秒足以够销毁一个进程 print(p.is_alive()) # True 就是判断进程是否存活 print(f'主进程的进程号:{os.getpid()}') print('主进程') ''' 运行结果: False 主进程的进程号:1108 主进程 '''
七、守护进程
# 如何理解进程 # 伴随着守护对象的存活而存活 死亡而死亡 from multiprocessing import Process import time def task(name): print('大内总管:%s存活' % name) time.sleep(3) print('大内总管:%s嗝屁' % name) if __name__ == '__main__': p = Process(target=task, args=('基佬',)) p.start() print('天子Jason寿终正寝!!!') # 当子进程不是主进程的守护进程的时候而执行个的互不影响 ''' 运行结果: 天子Jason寿终正寝!!! 大内总管:基佬存活 大内总管:基佬嗝屁 ''' from multiprocessing import Process import time def task(name): print('大内总管:%s存活' % name) time.sleep(3) print('大内总管:%s嗝屁' % name) if __name__ == '__main__': p = Process(target=task, args=('基佬',)) p.daemon = True # 将子进程设置为守护进程:主进程代码结束 子进程立刻结束 p.start() # p.daemon = True # 必须在start之前执行 如果不是在start前面就会报错 print('天子Jason寿终正寝!!!') ''' 运行结果: 天子Jason寿终正寝!!! '''
八、僵尸进程与孤儿进程
# 为什么主进程运行完了还要等子进程结束了 才会把整个代码结束呢? ''' 因为一个进程运行完代码的时候 相关资源没有并没有完全清空 如果就放那的话 很容易造成内存溢出 所以需要父进程进行收尾操作 参与回收 ''' """ 僵尸进程: 进程已经运行结束 但是相关的资源并没有完全清空 需要父进程参与回收 孤儿进程: 父进程意外死亡 子进程正常运行 该子进程就称之为孤儿进程 孤儿进程也不是没有人管 操作系统会自动分配福利院接收 """
九、作业
1.尝试将TCP服务端制作成并发效果
服务端
# 服务端 from multiprocessing import Process from socket import SOL_SOCKET, SO_REUSEADDR import socket server = socket.socket() server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) server.bind(('127.0.0.1', 8081)) server.listen(5) def task(sock): while True: data = sock.recv(1024) print(data.upper().decode('utf8')) sock.send(data.upper()) if __name__ == '__main__': while True: sock, address = server.accept() p = Process(target=task, args=(sock,)) p.start()
客户端1
# 客户端1 import socket client = socket.socket() client.connect(('127.0.0.1', 8081)) while True: client.send(b'hello jason') data = client.recv(1024) print(data.decode('utf8'))
客户端2
import socket client = socket.socket() client.connect(('127.0.0.1', 8081)) while True: client.send(b'hello tony') data = client.recv(1024) print(data.decode('utf8'))
客户端3
import socket client = socket.socket() client.connect(('127.0.0.1', 8081)) while True: client.send(b'hello kevin') data = client.recv(1024) print(data.decode('utf8'))