僵尸进程与孤儿进程、守护进程、互斥锁、消息队列、进程间数据交互、生产与消费者模型、线程理论与实现相关
2022-01-16 21:05
573 查看
昨日内容回顾
- 操作系统发展史
1.穿孔卡片时代 cpu 利用率极低 2.联机批处理系统 cpu效率有所提升 3.脱机批处理系统 cpu效率极大提升(也是现代计算机雏形) # 多道技术: 串行:多个任务依次排队执行 多道:切换 + 保存状态
- 进程理论
1.程序与进程的区别 程序是死的 进程是活的 2.进程的调度算法 先来先服务 短作业优先 时间片轮转法 + 多级反馈队列 3.进程的三状态 就绪态 运行态 阻塞态(只有经过就绪态 进程才能进入运行态) 4.任务的提交方式 同步: 提交完成任务之后 原地等待任务执行结果 期间不做其他任何事 异步: 提交完任务之后 不原地等待 结果由反馈机制反馈(异步回调机制) 5.进程的状态 阻塞态 :阻塞态 非阻塞态 :就绪态 运行态 '''高效的程序 意味着尽量一直处于非阻塞态'''
- 开启进程的多种方式
.start() 异步提交 告知操作系统开设进程
- 进程join方法
.join() 主进程等待子进程运行结束之后再继续执行 ''' 执行多个进程时 在等待时间最长的进程期间 其他的进程也在等待 '''
- 进程对象方法
1.获取进程号 current_process 查看进程号 os.getpid() 查看进程号 os.getppid() 查看父进程进程号 2.获取进程名称 p.name 3.杀死进程 p.terminate() 杀死子进程 4.判断进程是否存活 p.is_alive() 判断进程是否存活 '''3/4要结合使用'''
- 进程间数据默认是隔离的
关键字: global、nonlocal 名称空间概念
今日内容概要
- 僵尸进程与孤儿进程
- 守护进程
- 互斥锁
- 消息队列
- 实现进程间数据交互
- 生产者和消费者模型
- 线程理论
内容详细
1、僵尸进程与孤儿进程
# 1.僵尸进程 进程代码运行结束之后 其实并没有真正的结束 因为要等待子进程结束后 由主进程回收掉子进程资源 之后才能真正的结束 # 2.孤儿进程(属于有害的 会造成资源浪费) 主进程已经死亡(非正常死亡) 但是子进程还在运行
2、守护进程
# 守护进程: 就是守护着某个(子)进程 一旦这个(主)进程结束 那么被守护的(子)进程也会随之结束(无论被守护进程是否执行完毕) import time from multiprocessing import Process def test(name): print('太监总管:%s 开始执行' % name) time.sleep(3) print('太监总管:%s 结束了' % name) if __name__ == '__main__': p = Process(target=test, args=('jason', )) p.daemon = True # 设置为守护进程 必须要写在启动进程start()上面 p.start() print('皇帝jason寿终正寝了') time.sleep(0.5) 执行结果: 皇帝jason寿终正寝了 太监总管:jason 开始执行 """ 子进程并没有执行完毕 而是随着主进程的结束直接结束了 """
3、互斥锁
# 1.问题: 并发情况下操作同一份数据 极其容易造成数据错乱 import json import time from multiprocessing import Process import random # 模仿12306票务系统 # 先看票 def search(name): with open(r'a.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'a.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'a.txt', 'w', encoding='utf8') as f: json.dump(data_dict, f) print('%s 买票成功' % name) else: print('没有票啦啦啦') def run(name): search(name) buy(name) if __name__ == '__main__': for i in range(1, 11): # 假设十个人来买票 p = Process(target=run, args=('用户%s'% i, )) p.start() ''' 数据库: {"ticket_num": 1} ''' 执行结果: # 数据错乱 用户3查看余票为:1张 用户1查看余票为:1张 用户2查看余票为:1张 用户4查看余票为:1张 用户5查看余票为:1张 用户6查看余票为:1张 用户7查看余票为:1张 用户8查看余票为:1张 用户9查看余票为:1张 用户10查看余票为:1张 用户7 买票成功 用户5 买票成功 用户6 买票成功 用户1 买票成功 用户3 买票成功 用户9 买票成功 用户8 买票成功 用户10 买票成功 用户4 买票成功 用户2 买票成功 # 2.解决: 将并发变成串行 虽然降低了效率但是提升了数据的安全 锁就可以实现将并发变成串行的效果 行锁:锁定被某个用户查看的某行数据 其他用户同时间无法操作 表锁:锁定被某个用户查看的某个数据表 其他用户同时间无法操作 ''' 使用锁的注意事项: 1.在主进程中产生 交由子进程使用 2.一定要在需要的地方加锁 千万不要随意加 3.不要轻易的使用锁(容易出现死锁现象) 在以后的编程生涯中 几乎不会解除到自己操作锁的情况 此处仅作了解内部原理即可 ''' import json import time from multiprocessing import Process, Lock import random # 模仿12306票务系统 # 先看票 def search(name): with open(r'a.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'a.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'a.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() 执行结果: # 数据正常 用户1:查看余票为:1张 用户2:查看余票为:1张 用户4:查看余票为:1张 用户5:查看余票为:1张 用户3:查看余票为:1张 用户6:查看余票为:1张 用户7:查看余票为:1张 用户8:查看余票为:1张 用户9:查看余票为:1张 用户10:查看余票为:1张 用户1 买票成功 没有票啦啦啦 没有票啦啦啦 没有票啦啦啦 没有票啦啦啦 没有票啦啦啦 没有票啦啦啦 没有票啦啦啦 没有票啦啦啦 没有票啦啦啦
4、消息队列
# Queue 等待数 from multiprocessing import Queue 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()) # True 判断队列中数据是否满了 # q.put(666) # 如果加入该行以及更多行 则结果会一直卡住 直至有空位才会输出结果 print('我在这') # 最大等待数为5个及以内 才会输出结果 """ 输出结果: 空 会卡住原地等待 """ # 取数据 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()) # 再取数据时 如果已经没有数据了 会直接报错 """ 数据只有五个时 输出结果: 我在这 111 222 333 444 555 """ """ full和get_nowait和empty能否用于多进程情况下的精确使用 不能!!! 队列的使用就可以打破进程间默认无法通信的情况 """
5、ipc机制
# 1.主进程与子进程的数据交互 from multiprocessing import Queue, Process def consumer(q): print(q.get()) # 获取到了主进程数据 q.put('子进程的数据') if __name__ == '__main__': q = Queue() q.put('主进程的数据') p = Process(target=consumer, args=(q, )) p.start() p.join() print(q.get()) # 获取到了子进程数据 print('主') 执行结果: 主进程的数据 子进程的数据 主 # 2.子进程与子进程数据交互 from multiprocessing import Queue, Process def producer(q): q.put('子进程producer的数据') 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() 执行结果: 子进程C取得数据 子进程producer的数据
6、生产者消费者模型
""" 生产者: 负责产生数据(做包子的) 消费者: 负责处理数据(吃包子的) 该模式需要解决供需不平衡现象 """ from multiprocessing import Process, Queue, JoinableQueue import time import random def producer(name, food, q): for i in range(5): 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)) q.task_done() # 检测队列还有多少数据没被取 if __name__ == '__main__': # q = Queue() q = JoinableQueue() p = Process(target=producer, args=('厨师长jsaon', '酱猪蹄', q)) p1 = Process(target=producer, args=('印度阿三', '印度飞饼', q)) p2 = Process(target=producer, args=('泰国唐仁', '榴莲饼', q)) c = Process(target=consumer, args=('小明', q)) p.start() p1.start() p2.start() c.daemon = True c.start() p.join() p1.join() p2.join() q.join() # 等待队列中所有的数据被取干净 print('主') ''' 执行结果: 在数据被取完之后 主进程自动停止 不会卡住等待 '''
7、线程理论
# 什么是线程 进程其实就是一个资源单位 真正被CPU执行的 其实是进程里面的线程 进程间数据默认是隔离的 但是同一个进程内的多个线程数据是共享的 ''' 进程类似于工厂 线程类似于工厂里面的一条条流水线 所有的进程肯定至少含有一条线程 '''
8、开设线程的两种方式
# 进程 """ 开设进程的操作: 1.重新申请一块内存空间 2.将所需的资源全部导入 """ # 线程 """ 开设线程两个步骤都不需要 所以开设线程消耗的资源 远比开设进程消耗的资源要少 """ # 开设线程的方式一: from threading import Thread import time def test(name): print('%s 已经开启' % name) time.sleep(3) print('%s 已经关闭' % name) t = Thread(target=test, args=('jason', )) t.start() print('主') # 开设线程的方式二: from threading import Thread import time class MyClass(Thread): def __init__(self, name): super().__init__() # 因为源代码有__init__ 所以要再用类去调用 避免源代码不完整 self.name = name def run(self): print('%s正在开启' % self.name) time.sleep(3) print('%s正在关闭' % self.name) obj = MyClass('jason') obj.start() print('主') '''执行结果与方式一一致'''
9、线程对象的其他方法
1.join()方法 from threading import Thread import time def test(name): print('%s 已经开启' % name) time.sleep(3) print('%s 已经关闭' % name) t = Thread(target=test, args=('jason', )) t.start() t.join() # 主线程等待子线程运行结束之后 再继续运行 print('主') 2.获取进程号(验证同一个进程内可以开设多个线程) 3.active_count统计当前正在活跃的线程数 from threading import Thread, active_count import time import os def test(name): print(os.getpid()) # 13428 所有的子线程同属一个进程 所以进程号都一致 print('%s 已经开启' % name) time.sleep(3) print('%s 已经关闭' % name) t = Thread(target=test, args=('jason', )) t.start() print(os.getpid()) # 13428 所有的子线程同属一个进程 所以进程号都一致 print(active_count()) # 2 自带的主线程与开设的一条子线程 print('主') 4.current_thread from threading import Thread, active_count, current_thread import time import os def test(name): print(os.getpid()) # 13428 所有的子线程同属一个进程 所以进程号都一致 print(current_thread().name) # Thread-1 子线程 print('%s 已经开启' % name) time.sleep(3) print('%s 已经关闭' % name) t = Thread(target=test, args=('jason', )) t.start() # t.join() # 主线程等待子线程运行结束之后 再继续运行 print(os.getpid()) # 13428 所有的子线程同属一个进程 所以进程号都一致 print(active_count()) # 2 自带的主线程与开设的一条子线程 print(current_thread().name) # MainThread 主线程 print('主')
10、守护线程
""" 主线程的结束意味着整个进程的结束 所以主线程需要等待里面所有非守护线程的结束才能结束!!! """ from threading import Thread import time def test(name): print('%s 正在启动'% name) time.sleep(3) print('%s 已经结束'% name) if __name__ == '__main__': t = Thread(target=test, args=('线程1', )) t.daemon = True # 守护线程 t.start() print('主') 执行结果: 线程1 正在启动 主 # 例题: from threading import Thread import time def foo(): print(123) time.sleep(1) print("end123") def bar(): print(456) time.sleep(3) print("end456") if __name__ == '__main__': t1=Thread(target=foo) t2=Thread(target=bar) t1.daemon=True t1.start() t2.start() print("main-------") """ 执行结果: 123 456 main------- end123 end456 主线程等待所有非守护线程执行结束才会结束 所有结果全部打印 如果将: foo time.sleep(3) bar time.sleep(1) 执行结果: 123 456 main------- end456 因为 bar子线程比 foo子线程结束的早 所以主线程结束 foo子线程还没有执行完毕 只打印了启动信息 就随着主线程守护结束了 """
11、线程数据共享
from threading import Thread money = 100 def test(): global money # 更改全局信息 money = 999 t = Thread(target=test) t.start() t.join() print(money) # 999 进程的主线程获取到了子线程的数据信息
12、线程互斥锁
1.问题:多线程同时访问数据会错乱 from threading import Thread import time num = 100 def test(): global num # 先获取num的数值 tmp = num # 模拟延迟效果 time.sleep(1) # 修改数值 tmp -= 1 num = tmp t_list = [] for i in range(100): # 让100个人去操作数据 t = Thread(target=test) t.start() t_list.append(t) # 确保所有的子线程全部结束 for t in t_list: t.join() print(num) # 主线程打印 结果 99(理论上100个人操作 结果应该为0) 2.解决:将并发变成串行 from threading import Thread import time from multiprocessing import Lock 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): # 让100个人去操作数据 t = Thread(target=test, args=(mutex, )) t.start() t_list.append(t) # 确保所有的子线程全部结束 for t in t_list: t.join() print(num) # 主线程打印 结果 0
13、TCP服务端实现并发
# 客户端client: import socket client = socket.socket() client.connect(('127.0.0.1', 8080)) while True: msg = input('>>>:') client.send(msg.encode('utf8')) data = client.recv(1024) print(data.decode('utf8')) # 服务端service: import socket from threading import Thread 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'big baby') except ConnectionError as e: print(e) break sock.close() while True: sock, addr = server.accept() print(addr) # 开设多线程 t = Thread(target=talk, args=(sock, )) t.start() 执行结果: 服务端可以同时对多个客户端提供服务 实现了并发效果
相关文章推荐
- python-守护进程-互斥锁-死锁-IPC-队列-生成者消费者模型
- 多进程(了解):守护进程,互斥锁,信号量,进程Queue与线程queue(生产者与消费者模型)
- 多线程实现生产者消费者模型,以及线程和进程的回顾
- java线程间通信[实现不同线程之间的消息传递(通信),生产者和消费者模型]
- 多线程+阻塞队列实现生产者-消费者模型获取队列数据问题
- 【Linux】线程总结:线程同步 -互斥锁,条件变量,信号量实现多生产者多消费者模型
- 用生产者消费者模型实现的线程安全环形队列
- 在Windows系统上实现轻量级的线程间及进程间消息队列
- Android进程和线程 --消息队列模型 (Looper, MessageQueue, Handler) (1)
- Android进程和线程 --消息队列模型--ThreadLocal (3)(2015-12-02 19:41)
- Android进程和线程 --消息队列模型--Looper (2)(2015-12-02 19:41)
- 用线程和消息队列实现连个进程间的通讯
- 线程/GIL/线程锁/信号量/守护进程/Event事件/queue队列/生产者消费者模型
- 11.python并发入门(part8 基于线程队列实现生产者消费者模型)
- 在Windows系统上实现轻量级的线程间及进程间消息队列
- 基于线程实现的生产者消费者模型(Object.wait(),Object.notify()方法)
- 用消息队列模拟生产者消费者模型
- 如何在C#用WM_COPYDATA消息来实现两个进程之间传递数据
- linux 共享内存shm_open实现进程间大数据交互
- 安卓的进程与线程及其相关代码实现