进程,互斥锁,生产者消费者,线程
一、僵尸进程与孤儿进程
'''
僵尸进程(有坏处):
- 在子进程结束后,主进程没有正常结束,子进程的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