进程,互斥锁,生产者消费者,线程
程序员文章站
2022-06-10 22:31:24
进程,互斥锁,生产者消费者,线程 一、僵尸进程与孤儿进程 代码演示 二、子进程回收的两种方式 代码演示 三、进程守护 演示 四、进程间数据是隔离的 演示 五、进程互斥锁 演示 六、队列 演示 七、IPC(进程间通信) 演示 八、生产者与消费者 演示 子线程守护 十、线程互斥锁 演示 ......
进程,互斥锁,生产者消费者,线程
一、僵尸进程与孤儿进程
- 代码演示
''' 僵尸进程(有坏处): - 在子进程结束后,主进程没有正常结束,子进程的pid不会被回收。 缺点: - 操作系统中pid号是有限的,比如子进程pid号无法正常回收,则会占用pid号。 - 资源浪费 - 若pid号满了,则无法创建新的进程。 孤儿进程(没有坏处): - 在子进程没有结束时,主进程没有“正常结束”,子进程pid不会被回收。 - 操作系统优化机制(孤儿院): 当主进程意外终止,操作系统会检测是否有正在运行的子进程,会将他们放入孤儿院,让操作系统帮你自动回收。 ''' #孤儿院进程 from multiprocessing import process from multiprocessing import current_process #在子进程中调用,可以拿到子进程对象.pid可以获取pid号 #在主进程中调用,可以拿到主进程对象.pid可以获取pid号 import os import time def task(): print(f'start...{current_process().pid}') time.sleep(1000) print(f'end...{os.getpid()}') print('子进程结束啦啊...') if __name__ == '__main__': p = process(target=task) p.start() print(f'进入主进程的io--->{current_process().pid}') time.sleep(4) print(f'进入主进程的io--->{os.getpid()}') #主进程结束 print('主进程结束...') print(f'查看主进程{os.getpid()}') f = open('yafenghandsome.txt')#此时主进程没有正常结束 #僵尸进程 from multiprocessing import process from multiprocessing import current_process #在子进程中调用,可以拿到子进程对象.pid可以获取pid号 #在主进程中调用,可以拿到主进程对象.pid可以获取pid号 import os import time def task(): print(f'start...{current_process().pid}') time.sleep(1) print(f'end...{os.getpid()}') print('子进程结束啦啦...~~~') if __name__ == '__main__': p = process(target=task) p.start() print(f'进入主进程的io--->{current_process().pid}') time.sleep(5) print(f'进入主进程的io--->{os.getpid()}') print('主进程结束...') print(f'查看主主进程{os.getppid()}') f = open('yafeng6666.txt')
二、子进程回收的两种方式
- 代码演示
from multiprocessing import process import time # def task(): # print('start...') # time.sleep(2) # print('end...') # # # # #方式一join让主进程等待子进程结束,并回收子进程资源,主进程再结束并回收资源。 # if __name__ == '__main__': # p = process(target=task) # # #告诉操作系统帮你开启子进程 # p.start() # p.join() # # time.sleep(3) # # #主进程结束 # print('主进程结束...') # 方式二主进程 “正常结束” ,子进程与主进程一并被回收资源。 def task(): print('start...') time.sleep(2) print('end...') if __name__ == '__main__': p = process(target=task) p.start() time.sleep(1) print('主进程结束...')
三、进程守护
- 演示
''' 守护进程: 当主进程结束时,子进程也必须结束,并回收。 ''' from multiprocessing import process import time def demo(name): print(f'start....{name}') time.sleep(100) print(f'end...{name}') print('子进程结束啦啊....') if __name__ == '__main__': p = process(target=demo, args=('童子军jason1号',)) # 守护进程必须在p.start()调用之前设置 p.daemon = true # 将子进程p设置为守护进程 p.start() time.sleep(1) print('皇帝驾崩啦...')
四、进程间数据是隔离的
- 演示
from multiprocessing import process import time ''' 进程间数据是隔离的 ''' number = 10 def func(): global number print('子进程1号') number = 100 def func2(number): print('子进程2号') number += 100 if __name__ == '__main__': p_obj = process(target=func) p_obj2 = process(target=func2, args=(number, )) p_obj.start() p_obj2.start() p_obj.join() p_obj2.join() time.sleep(1) print(f'主进程,{number}') #110 ---> 证明进程间数据不是隔离的 #10 ---> 证明进程间数据是隔离的 # 子进程1号 # 子进程2号 # 主进程,10
五、进程互斥锁
- 演示
from multiprocessing import process from multiprocessing import lock import random import time import json #抢票例子 #1、查看余票 def search(name): #1、读取data.json文件中的数据 with open('data.json', 'r', encoding='utf-8') as f: data_dic = json.load(f) print(f'用户{name}查看余票,余票还剩:{data_dic.get("number")}!') #2、若有余票,购买成功,票数会减少 def buy(name): with open('data.json', 'r', encoding='utf-8') as f: data_dic = json.load(f) #谁先进入这一步代表最先抢到票 if data_dic.get('number') > 0: data_dic['number'] -= 1 time.sleep(random.randint(1, 3)) with open('data.json', 'w', encoding='utf-8') as f: json.dump(data_dic, f) print(f'用户{name}, 抢票成功!') else: print(f'用户{name}, 抢票失败!') def run(name, lock): #假设1000个用户过来都可以立马查看余票 search(name) lock.acquire() #加锁 buy(name) lock.release() #释放锁 if __name__ == '__main__': lock = lock() #开启多进程,实现并发 for line in range(10): p_obj = process(target=run, args=(f'jason{line}', lock)) p_obj.start() # 用户jason2查看余票,余票还剩:1! # 用户jason0查看余票,余票还剩:1! # 用户jason3查看余票,余票还剩:1! # 用户jason4查看余票,余票还剩:1! # 用户jason1查看余票,余票还剩:1! # 用户jason5查看余票,余票还剩:1! # 用户jason6查看余票,余票还剩:1! # 用户jason7查看余票,余票还剩:1! # 用户jason8查看余票,余票还剩:1! # 用户jason2, 抢票成功! # 用户jason0, 抢票失败! # 用户jason3, 抢票失败! # 用户jason4, 抢票失败! # 用户jason1, 抢票失败! # 用户jason5, 抢票失败! # 用户jason6, 抢票失败! # 用户jason7, 抢票失败! # 用户jason8, 抢票失败! # 用户jason9查看余票,余票还剩:0! # 用户jason9, 抢票失败!
六、队列
- 演示
from multiprocessing import queue #multiprocessing 提供队列,先进先出 from multiprocessing import joinablequeue #joinablequeue提供队列,先进先出 import queue #第一种 # q_obj1 = queue(5) #此处的5指的是队列中只能存放5份数据 # # #添加数据到队列中 # q_obj1.put('热巴!') # print('添加1个啦') # q_obj1.put('胡歌!') # print('添加2个啦') # q_obj1.put('亚峰!') # print('添加3个啦') # q_obj1.put('科比!') # print('添加4个啦') # q_obj1.put('詹姆斯!') # print('添加5个啦') # #注意:put只要队列满了,会进入阻塞状态 # q_obj1.put('sean') # print('我想添加第六个看会不会报错') #put_nowait:只要队列满了就会报错 # q_obj1.put_nowait('sean') # 添加1个啦 # 添加2个啦 # 添加3个啦 # 添加4个啦 # 添加5个啦 # queue.full #get:只要队列中有数据,就能获取数据,若没有会进入阻塞状态 # print(q_obj1.get()) # print(q_obj1.get()) # print(q_obj1.get()) # print(q_obj1.get()) # print(q_obj1.get()) #get_nowait:若队列中没有数据获取则会报错 # print(q_obj1.get_nowait()) # 热巴! # 胡歌! # 亚峰! # 科比! # 詹姆斯! # _queue.empty # #第二种方式 # q_obj1 = joinablequeue(5) #此处的5指的是队列中只能存放5份数据 # #添加数据到队列中 # q_obj1.put('热巴!') # print('添加1个啦') # q_obj1.put('胡歌!') # print('添加2个啦') # q_obj1.put('亚峰!') # print('添加3个啦') # q_obj1.put('科比!') # print('添加4个啦') # q_obj1.put('詹姆斯!') # print('添加5个啦') # # # #注意:put只要队列满了,会进入阻塞状态 # # q_obj1.put('sean') # # print('我想添加第六个看会不会报错') # # # put_nowait:只要队列满了就会报错 # # q_obj1.put_nowait('sean') # # # # 添加1个啦 # # 添加2个啦 # # 添加3个啦 # # 添加4个啦 # # 添加5个啦 # # queue.full # # # # get:只要队列中有数据,就能获取数据,若没有会进入阻塞状态 # # # print(q_obj1.get()) # print(q_obj1.get()) # print(q_obj1.get()) # print(q_obj1.get()) # print(q_obj1.get()) # # # # get_nowait:若队列中没有数据获取则会报错 # # print(q_obj1.get_nowait()) # # # # 热巴! # # 胡歌! # # 亚峰! # # 科比! # # 詹姆斯! # # _queue.empty # 第三种方式 q_obj1 = queue.queue(5) #此处的5指的是队列中只能存放5份数据 #添加数据到队列中 q_obj1.put('热巴!') print('添加1个啦') q_obj1.put('胡歌!') print('添加2个啦') q_obj1.put('亚峰!') print('添加3个啦') q_obj1.put('科比!') print('添加4个啦') q_obj1.put('詹姆斯!') print('添加5个啦') #注意:put只要队列满了,会进入阻塞状态 #q_obj1.put('sean') #print('我想添加第六个看会不会报错') # put_nowait:只要队列满了就会报错 #q_obj1.put_nowait('sean') # 添加1个啦 # 添加2个啦 # 添加3个啦 # 添加4个啦 # 添加5个啦 # queue.full #get:只要队列中有数据,就能获取数据,若没有会进入阻塞状态 print(q_obj1.get()) print(q_obj1.get()) print(q_obj1.get()) print(q_obj1.get()) print(q_obj1.get()) #get_nowait:若队列中没有数据获取则会报错 #print(q_obj1.get_nowait()) # 热巴! # 胡歌! # 亚峰! # 科比! # 詹姆斯! # _queue.empty
七、ipc(进程间通信)
- 演示
from multiprocessing import process from multiprocessing import joinablequeue import time ''' 通过队列可实现进程间通信 ''' def task1(q): x = 100 q.put(x) print('添加数据') time.sleep(3) print(q.get()) def task2(q): #想要在task2中获取task1中的x res = q.get() print(f'获取的数据{res}') q.put(9527) if __name__ == '__main__': q = joinablequeue(10) #产生两个不同的子进程 p1 = process(target=task1, args=(q, )) p2 = process(target=task2, args=(q, )) p1.start() p2.start() # 添加数据 # 获取的数据100 # 9527
八、生产者与消费者
- 演示
from multiprocessing import joinablequeue from multiprocessing import process import time #生产者:生产数据---> 队列 def producer(name, food, q): msg = f'{name}生产了{food}食物' #生产一个食物添加到牌队列中去 q.put(food) print(msg) #消费者: 使用数据 <--- 队列 def customer(name, q): while true: try: time.sleep(0.5) #若报错,则跳出循环 food = q.get_nowait() msg = f'{name}吃了{food}食物' print(msg) except exception: break if __name__ == '__main__': q = joinablequeue() #创建10个生产者 for line in range(10): p1 = process(target=producer, args=('yafeng', f'高级食物{line}', q)) p1.start() #创建两个消费者 c1 = process(target=customer, args=('jason', q)) c2 = process(target=customer, args=('sean', q)) c1.start() c2.start() # yafeng生产了高级食物1食物 # yafeng生产了高级食物0食物 # yafeng生产了高级食物2食物 # yafeng生产了高级食物3食物 # yafeng生产了高级食物4食物 # yafeng生产了高级食物5食物 # yafeng生产了高级食物6食物 # yafeng生产了高级食物8食物 # yafeng生产了高级食物7食物 # yafeng生产了高级食物9食物 # jason吃了高级食物1食物 # sean吃了高级食物0食物 # jason吃了高级食物2食物 # sean吃了高级食物3食物 # jason吃了高级食物4食物 # sean吃了高级食物5食物 # jason吃了高级食物6食物 # sean吃了高级食物8食物 # jason吃了高级食物7食物 # sean吃了高级食物9食物
九、线程以及守护线程
- 开启线程的方式以及理论知识
''' 线程: 1、什么是线程? 进程:资源单位 线程:执行单位 线程与进程都是虚拟的概念,为了更好表达某种食物 注意:开启一个进程,一定会自带一个线程,线程才是真正的执行者。 2、为什么要使用线程? 节省资源的占用 - 开启进程: -1)会产生一个内存空间,申请一块子资源 -2) 会自带一个主线程 -3)开启子进程的速度要比开启子线程的速度慢 - 开启线程 -1);一个进程内可以开启多个线程,从进程的内存空间中申请执行单位 -2):节省资源 - 开启三个线程 - 从一个内存资源中,申请三个小的执行单位 - io密集型用:多线程 - io(时间由用户定): - 阻塞:切换 + 保存状态 - 计算密集型用:多进程 - 计算(时间由操作系统定): - 计算时间很长 ---> 切换 + 保存状态 注意:进程与进程之间数据是隔离的,线程与线程之间数据是共享的 3、怎么使用线程? from threading import thread ''' from threading import thread import time number = 1000 # #启动线程的方式一: # def task(): # global number # number = 100 # print('start...') # time.sleep(1) # print('end...') # # # if __name__ == '__main__': # #开启一个子线程 # t = thread(target=task) # t.start() # # t.join() # print('主进程(主线程)...') # print(number) # # # 主进程(主线程)... # # 100 # # start... # # end... # #开启线程的方式二: # class mythread(thread): # def run(self): # print('start...') # time.sleep(1) # print('end...') # # # if __name__ == '__main__': # #开启一个子线程 # t = mythread() # t.start() # # t.join() # print('主进程(主线程)...')
- 子线程守护
from threading import current_thread number = 1000 def task(): global number number = 100 print(f'start....{current_thread().name}') time.sleep(3) print(f'end....{current_thread().name}') if __name__ == '__main__': for line in range(10): t = thread(target=task) #加上守护线程:主进程结束,代表主线程也结束,子线程可能未被回收 t.daemon = true t.start() print(f'主进程(主线程)....{current_thread().name}') print(number)
十、线程互斥锁
- 演示
from threading import lock from threading import thread import time #开启10个线程,对一个数据进行修改 number = 100 def task(): global number lock.acquire() number2 = number time.sleep(1) number = number2 - 1 lock.release() if __name__ == '__main__': lock = lock() list1 = [] for line in range(10): t = thread(target=task) t.start() list1.append(t) for t in list1: t.join() print(number) #>>>> 90
上一篇: 当面试官问你:如何进行性能优化?
下一篇: 利用缓存读取文件提高性能