# homework """ 无阻塞状态下进程是否提高效率实验 单个线程执行版本 执行时间: 7.585954189300537 """ from time import time # 求函数运行时间装饰器 def timeis(func): def wapper(*args, **kwargs): begin = time() res = func(*args, **kwargs) print("执行时间:", time() - begin) return res return wapper def is_prime(num): if num <= 1: return False for i in range(2, num // 2 + 1): if num % i == 0: return False return True @timeis def process_1(): prime = [] for i in range(1, 100001): if is_prime(i): prime.append(i) return prime print(sum(process_1())) """ 无阻塞状态下多进程是否提高效率实验 开启单个进程的情况下,双核cpu只有一个核心在运行 开启多个进程的情况下,cpu只有双核也只能提供双核,因此无论开启 多少个进程,由于物理受限,执行时间也只能减半。 """ from time import time from multiprocessing import Process, Queue q = Queue(10) # 求函数运行时间的装饰器 def timeis(func): def wapper(*args, **kwargs): begin = time() res = func(*args, **kwargs) print("执行时间:", time() - begin) return res return wapper def is_prime(num): if num <= 1: return False for i in range(2, num // 2 + 1): if num % i == 0: return False return True # 自定义进程类 # 多个进程求解 class Prime(Process): def __init__(self, begin, end): self.begin = begin self.end = end super().__init__() def run(self): prime = [] for i in range(self.begin, self.end): if is_prime(i):#类里面调用类外方法 prime.append(i) # print(sum(prime)) q.put(sum(prime)) @timeis def process_4(): jobs = [] sumall = [] for i in range(1, 100001, 25000): p = Prime(i, i + 25000) jobs.append(p) p.start() sumall.append(q.get()) for i in jobs: i.join() return sum(sumall) # 开启1个进程执行时间: # process_1() # 开启4个进程执行时间: print(process_4()) # 开启10个进程执行时间: # process_10()
2.2.5 进程间通信
-
必要性: 进程间空间独立,资源不共享,此时在需要进程间数据传输时就需要特定的手段进行数据通信。解决了进程没有返回值问题。其实我们还可以采用第三方例如文件数据库套接字等也能实现通信哦。
-
常用进程间通信方法:消息队列,套接字,文件数据库等第三方量。
-
消息队列使用
-
通信原理: 在内存中开辟独立内存空间,建立队列模型,进程通过队列将消息存入,或者从队列取出完成进程间通信。
-
实现方法
from multiprocessing import Queue #先进先出策略 q = Queue(maxsize=0) 功能: 创建队列对象 参数:最多存放消息(即任何数据类型与对象)个数,不写默认根据内存开辟多少来存 返回值:队列对象 q.put(data) 功能:向队列存入消息 放满了程序就阻塞 参数:data 要存入的内容 q.get() 功能:从队列取出消息 读取不出程序就阻塞 返回值: 返回获取到的内容--先进先出原则 q.full() 判断队列是否为满 q.empty() 判断队列是否为空 q.qsize() 获取队列中消息个数 q.close() 关闭队列
-
""" 进程之间相互通信案例 父进程向子进程发送指令,进行控制 python对亲缘之间的进程通信支持例如父子进程与兄弟进程较好 不相干之间的进程通信就需要使用套接字 """ from multiprocessing import Queue, Process # 一定在父进程创建的消息队列然后各个子进程直接使用 q = Queue(5) def handle(): while True: cmd = q.get() if cmd == "1": print("启动摄像头") elif cmd == "2": print("启动麦克风") else: print("结束") break # 父子进程共用一个输出控制台 p = Process(target=handle) p.start() while True: cmd = input(">>") q.put(cmd)
群聊聊天室
【1】 有人进入聊天室需要输入姓名,姓名不能重复
【2】 有人进入聊天室时,其他人会收到通知:Lucy 进入了聊天室
【3】 一个人发消息,其他人会收到: Lucy : 一起出去玩啊。
【4】 有人退出聊天室,则其他人也会收到通知 : Lucy 退出了聊天室
【5】 扩展功能:服务器可以向所有用户发送公告: 管理员消息: 大家好,欢迎进入聊天室。
""" 聊天室服务端 """ from socket import * from multiprocessing import Process ADDR = ("0.0.0.0", 8888) userinfo = {} def goin(sock, name, addr): if name in userinfo: sock.sendto(b"not ok", addr) return sock.sendto(b"ok", addr) msg = "欢迎 %s 加入聊天室" % name for key, value in userinfo.items(): sock.sendto(msg.encode(), value) userinfo[name] = addr print(userinfo) def chat(sock, name1, content): msg = "%s : %s" % (name1, content) for name, address in userinfo.items(): if name1 != name: sock.sendto(msg.encode(), address) def goout(sock, name): del userinfo[name] msg = " %s 离开了聊天室" % name for key, value in userinfo.items(): sock.sendto(msg.encode(), value) # 子进程接收各种请求分情况讨论 def hanndle(udp_socket): while True: request, addr = udp_socket.recvfrom(1024) temp = request.decode().split(" ", 2) # 切2次 if temp[0] == "GOIN": # temp[0] = GOIN temp[1]=name goin(udp_socket, temp[1], addr) elif temp[0] == "CHAT": # temp[0] = CHAT temp[1]=name temp[2]=content chat(udp_socket, temp[1], temp[2]) elif request == "GOOUT": # temp[0] = GOOUT temp[1]=name goout(udp_socket, temp[1]) # 父进程发送管理员消息 def main(): # 创建udp网络循环模型 udp_socket = socket(type=SOCK_DGRAM) udp_socket.bind(ADDR)#在一个程序里父子进程可以共用一个地址与套接字,并且相互独立出去 p = Process(target=hanndle, args=(udp_socket,)) p.start() while True: content = input("管理员:") msg = "CHAT 管理员 " + content udp_socket.sendto(msg.encode(), ADDR) # 利用套接字把父进程消息递交个子进程 if __name__ == '__main__': main()
""" 聊天室客户端1 """ from socket import * from multiprocessing import Process ADDR = ("127.0.0.1", 8888) def goin(sock): while True: name = input("name->:") msg = "GOIN " + name sock.sendto(msg.encode(), ADDR) result, addr = sock.recvfrom(1024) if result == b"ok": print("新用户成功进入聊天室") return name elif result == b"not ok": print("该用户已存在聊天室,不要重复进入,请输入新用户") # 子进程:循环接收消息并打印 def recv_msg(sock): while True: data, addr = sock.recvfrom(1024) print(data.decode()) def send_msg(sock, name): while True: try: # 防止客户端异常退出 content = input("发言:") except KeyboardInterrupt: content = "##" if content == "##": msg = "GOOUT " + name sock.sendto(msg.encode(), ADDR) break msg = "CHAT %s %s" % (name, content) sock.sendto(msg.encode(), ADDR) def chat(sock, name): p = Process(target=recv_msg, args=(sock,), daemon=True) # 让子进程不再接收阻塞,随着父进程退出 p.start() send_msg(sock, name) def main(): udp_socket = socket(AF_INET, SOCK_DGRAM) udp_socket.bind(("0.0.0.0", 55880))#udp不长时间使用地址会变,最好绑定一下地址 #TCP不会,因为建立连接永久稳定 name = goin(udp_socket) chat(udp_socket, name) udp_socket.close() print("你已主动退出聊天室") if __name__ == '__main__': main()
编辑
2.3 线程 (Thread)
2.3.1 线程概述
-
什么是线程
【1】 线程被称为轻量级的进程,也是多任务编程方式
【2】 也可以利用计算机的多核cpu资源
【3】 线程可以理解为进程中再开辟的分支任务
-
线程特征
【1】 一个进程中可以包含多个线程,之前内容都是单进程。
【2】 线程也是一个运行行为,消耗计算机资源
【3】 一个进程中的所有线程共享这个进程的资源,进程中的线程资源需求按需分配。
【4】 多个线程之间的运行同样互不影响各自运行,同样是抢占关系。
【5】 线程的创建和销毁消耗资源远小于进程
编辑
2.3.2 多线程编程
-
线程模块: threading
编辑
-
创建方法
【1】 创建线程对象
from threading import Thread t = Thread() 功能:创建线程对象 参数:target 绑定线程函数 args 元组 给线程函数位置传参 kwargs 字典 给线程函数键值传参 daemon bool值,主线程退出时该分支线程也退出
【2】 启动线程
t.start()
【3】等待分支线程结束
p.join([timeout]) 功能:阻塞等待分支线程退出 参数:最长等待时间
""" 线程创建示例 一个进程开个线程叫主线程带个分支线程 进程共享资源,全局变量a可以在任何进程修改,其他进程读取的的a也随之改变 当下的两个线程属于同一个进程,他们的pid一样 """ import threading from time import sleep import os a = 1 # 线程执行函数 def music(): global a print("a =", a) a = 10000 for i in range(3): sleep(2) print("播放: 黄河大合唱") print(os.getpid()) #id thread = threading.Thread(target=music) thread.start() # 主线程 for i in range(4): sleep(1) print("播放:葫芦娃") # thread.join() print("a:", a) # 10000 print(os.getpid()) #都一样
编辑
前情回顾 1. 多任务编程 一个程序执行时能同时运行多个任务 作用 : 多个任务配合 提高执行效率 并行 并发 进程 线程 2. 多进程 不能知道是并行还是并发,充分相信操作系统分配机制 应用层只需要知道是同时就行,不管真假同时 multiprocessing p = Process(target=func) p.start() p.join() 创建一个子进程 创建多个子进程,多进程运行的想象力很重要 自定义进程类 3. 细节处理 os.getpid() os.getppid() sys.exit() 孤儿进程 僵尸进程 算法训练: 力扣 剑指offer 牛客网 算法爱好者(公众号) 算法导论 群聊聊天室 需求分析: 有人进入聊天室需要输入姓名,姓名不能重复 有人进入聊天室时,其他人会收到通知:Lucy 进入了聊天室 一个人发消息,其他人会收到: Lucy : 一起出去玩啊。 有人退出聊天室,则其他人也会收到通知 : Lucy 退出了聊天室 技术点设定 : C/S 保存用户信息 : {name:address}, [(name,addrss),] 网络 : udp 消息传输 : 客户端 --> 服务器 --> 其他客户端 客户端收发配置: 各自使用一个进程 功能划分和封装结构设计(函数,类,模块等结构设计) 进入聊天室 聊天 退出聊天室 通信协议 (请求协议设计) C/S B/S:浏览器服务端 请求类型 数据参数 进入聊天室 LOGIN name 聊天 CHAT name 内容 退出聊天室 EXIT 具体功能模块逻辑设计 框架结构 做一个功能测试一个 进入聊天室 客户端: 输入姓名 将姓名发送给服务端 等待一个结果 (是否能够进入) 打印结果 服务端: 接收姓名 判断姓名是否存在 存在 : 告知客户端不能进入 不存在 : 告知已经加入聊天 存储用户信息 (name,addr) 告知其他用户有人进入 聊天 客户端: 创建一个子进程 父进程循环输入消息并发送 子进程循环接收消息并打印 服务端: 接收消息 转发给其他客户端 退出聊天室 客户端 : 输入 ## 或者点 × 表示退出 服务端 : 接收请求 删除用户 告知其他人 优化完善 作业: 1. 聊天室梳理