僵尸进程
主进程代码运行完成之后不会直接结束,而是要等待所有子进程运行完回收它们的资源之后才能结束。
孤儿进程
主进程已经死亡(非正常死亡),而子进程还在运行中。
守护进程: 守护着某个进程,一但守护的进程结束,守护进程也会随之结束
from multiprocessing import Process import time def test(): print('守护进程 is running') time.sleep(3) print('守护进程 is over') if __name__ == '__main__': p = Process(target=test) # 把 p 进程设置为守护进程 p.daemon = True p.start() time.sleep(0.2) print('被守护的主进程') # 运行结果 守护进程 is running 被守护的主线程 # '守护进程 is over'不会执行,因为主进程结束后, p 进程也会结束
拿抢票举例,数据库中车票还剩一张,有一个购买功能,先用文件操作获取数据库中车票数量的数据,再进行减一运算再保存,如果是并发状态下执行该功能,可以有多个进程同时对数据修改成功,也就是有多个人同时购买同一张票成功了,造成数据安全问题
问题: 并发情况下操作同一份数据,极其容易造成数据错乱
解决措施:将并发变成串行,虽然降低了效率但是提升了数据的安全行性
使用锁的注意事项 在主进程中产生 交由子进程使用 1.一定要在需要的地方加锁 千万不要随意加 2.不要轻易的使用锁(死锁现象) # 在以后的编程生涯中 几乎不会解除到自己操作锁的情况
import json from multiprocessing import Process, Lock import time import random # 查票 def search(name): with open(r'data.txt', 'r', encoding='utf8') as f: data_dict = json.load(f) ticket_num = data_dict.get('ticket_num') print('%s查询余票:%s' % (name, ticket_num)) # 买票 def buy(name): # 先查票 with open(r'data.txt', 'r', encoding='utf8') as f: data_dict = json.load(f) ticket_num = data_dict.get('ticket_num') # 模拟一个延迟 time.sleep(random.random()) # 判断是否有票 if ticket_num > 0: # 将余票减一 data_dict['ticket_num'] -= 1 # 重新写入数据库 with open(r'data.txt', 'w', encoding='utf8') as f: json.dump(data_dict, f) print('%s: 购买成功' % name) else: print('不好意思 没有票了!!!') def run(name,mutex): search(name) mutex.acquire() # 抢锁 buy(name) mutex.release() # 释放锁 if __name__ == '__main__': mutex = Lock() for i in range(1, 11): p = Process(target=run, args=('用户%s' % i,mutex)) p.start()
队列:先进先出 from multiprocessing import Queue # 这条命令就是产生一个队列对象 q # q 可以调用 put() 方法往队列中添加数据,调用 get() 方法从队列中获取数据 q = Queue(5) # 括号内可以填写最大等待数 # 存放数据 q.put(111) q.put(222) # print(q.full()) # False 判断队列中数据是否满了 q.put(333) q.put(444) q.put(555) # print(q.full()) # q.put(666) # 超出范围原地等待 直到有空缺位置 # 提取数据 print(q.get()) print(q.get()) print(q.get()) # print(q.empty()) # 判断队列中是否还存在数据 print(q.get()) print(q.get()) # print(q.get()) # 没有数据之后原地等待直到有数据为止 print(q.get_nowait()) # 没有数据立刻报错 """ full和get_nowait能否用于多进程情况下的精确使用 不能!!! 因为这两个方法只能判断某一时间刻队列中的数据情况,但队列的数据是随时会有其它进程进行着改动的 队列的使用就可以打破进程间默认无法通信的情况 """
实现进程间的数据交互
原理:创建一个队列,所有进程都可以都可以对这个队列进行数据存储操作,有进程往队列中添加数据,别的进程就可以从中取出它存进去的数据,实现进程间的数据交互
疑问:难道创建一个列表,然后所有进程都操操作这个列表不可以实现同样的效果吗?当然不行,列表只能存在某一个进程的全局名称空间中,而进程之间默认无法进行数据交互,也就无法操作某个进程中的列表。
from multiprocessing import Queue, Process def producer(q): q.put("子进程p放的数据") def consumer(q): print('子进程c取的数据',q.get()) if __name__ == '__main__': q = Queue() p = Process(target=producer, args=(q,)) c = Process(target=consumer, args=(q,)) p.start() c.start() # q.put('主进程放的数据') # p = Process(target=consumer, args=(q,)) # p.start() # p.join() # print(q.get()) # print('主')
也就是利用 IPC 机制,实现进程之的数据交互
""" 生产者 负责产生数据(做包子的) 消费者 负责处理数据(吃包子的) 该模型需要解决恭喜不平衡现象 """ # JoinableQueue 产生的队列可以提供更多队列操作方法,如 task_done() 可以判断队列中的数据是否被获取干净,没有数据则不会再执行 get() 方法 from multiprocessing import Queue, Process, JoinableQueue import time import random def producer(name, food, q): for i in range(10): print('%s 生产了 %s' % (name, food)) q.put(food) time.sleep(random.random()) def consumer(name, q): while True: data = q.get() print('%s 吃了 %s' % (name, data)) # 判断队列中的数据是否被获取干净,没有数据则不会再执行 get() 方法 q.task_done() if __name__ == '__main__': # q = Queue() q = JoinableQueue() p1 = Process(target=producer, args=('大厨jason', '玛莎拉', q)) p2 = Process(target=producer, args=('印度阿三', '飞饼', q)) p3 = Process(target=producer, args=('泰国阿人', '榴莲', q)) c1 = Process(target=consumer, args=('班长阿飞', q)) p1.start() p2.start() p3.start() # 主进程执行结束之后会立刻结束 c1.daemon = True c1.start() # 主进程等待所有生产者生产完数据 p1.join() p2.join() p3.join() # 主进程等待队列中所有数据被取干净 q.join() # 等待队列中所有的数据被取干净 print('主')
什么是线程?
进程其实是一个资源单位,真正被CPU执行的其实是进程里面的线程
""" 进程就像工厂,线程就像工厂中的一条条流水线 所有进程肯定含有最少一个线程 """
进程间的数据默认是隔离的,但同一个进程中的多个线程之间的数据是可以共享的
""" 开设进程需要做哪些操作 1.重新申请一块内存空间 2.将所需的资源全部导入 开设线程需要做哪些操作 上述两个步骤都不需要 所以开设线程消耗的资源远比开设进程的少!!! """ from threading import Thread import time # 直接开启线程 def test(name): print('%s is running' % name) time.sleep(3) print('%s is over' % name) t = Thread(target=test, args=('jason',)) t.start() print('主') # 用类开设线程 class MyClass(Thread): def __init__(self,name): super().__init__() self.name = name def run(self): print('%s is running' % self.name) time.sleep(3) print('%s is over' % self.name) obj = MyClass('jason') obj.start() print('主线程')
1.join方法
2.获取进程号(验证同一个进程内可以开设多个线程)
3.active_count统计当前正在活跃的线程数
4.current_thread
""" 主线程的结束意味着整个进程的结束 所以主线程需要等待里面所有非守护线程的结束才能结束 """ from threading import Thread from multiprocessing import Process import time def foo(): print(123) time.sleep(3) print("end123") def bar(): print(456) time.sleep(1) print("end456") if __name__ == '__main__': t1=Thread(target=foo) t2=Thread(target=bar) t1.daemon=True t1.start() t2.start() print("main-------")
from threading import Thread money = 100 def test(): global money money = 999 t = Thread(target=test) t.start() t.join() print(money) # 执行结果 999 说明主进程与子进程之间数据共享
from threading import Thread, Lock from multiprocessing import Lock import time num = 100 def test(mutex): global num mutex.acquire() # 先获取num的数值 tmp = num # 模拟延迟效果 time.sleep(0.1) # 修改数值 tmp -= 1 num = tmp mutex.release() t_list = [] mutex = Lock() for i in range(100): t = Thread(target=test, args=(mutex,)) t.start() t_list.append(t) # 确保所有的子线程全部结束 for t in t_list: t.join() print(num)
开启多线程
import socket from threading import Thread from multiprocessing import Process server = socket.socket() server.bind(('127.0.0.1', 8080)) server.listen(5) def talk(sock): while True: try: data = sock.recv(1024) if len(data) == 0: break print(data.decode('utf8')) sock.send(data + b'gun dan!') except ConnectionResetError as e: print(e) break sock.close() while True: sock, addr = server.accept() print(addr) # 开设多进程或者多线程 t = Thread(target=talk, args=(sock,)) t.start()
train
1.自行百度搜索什么是"乐观锁与悲观锁" 2.自行百度搜索什么是"消息队列" 3.自行百度搜索什么是"二十三种设计模式" # 平时就可以练习算法(冒泡 二分 插入 快排 堆排 桶排) 笔纸写出来的:冒泡 二分 插入 快排 能够描述出内部原理的:堆排 桶排