day29_进程间的通信和线程
目录
1、进程互斥锁
让并发变成串行,牺牲了执行效率,保证了数据安全
在程序并发执行的情况下,需要修改同一数据时使用
使用方法:
# 导入模块
from multiprocessing import Lock
# 先获取Lock对象
mutex = Lock()
# 在需要上锁的方法前加锁
mutex.acquire()
# 目标方法
func()
# 在需要上锁的方法后解锁
mutex.release()
实例演示:
需求:
模拟抢票软件
1 查看余票
2 开始抢票
# 代码实现
import json
import time
from multiprocessing import Process
from multiprocessing import Lock
# 查看余票
def search(user):
with open('data.json', 'r', encoding='utf-8') as fr:
dic = json.load(fr)
print(f'用户{user}查看余票,还剩余{dic.get("ticket_num")}...')
# 抢票
def buy(user):
with open('data.json', 'r', encoding='utf-8') as fr:
dic = json.load(fr)
time.sleep(0.1) # 模拟网络延时
# 若有票就修改data数据
if dic.get('ticket_num') > 0:
dic['ticket_num'] -= 1
with open('data.json', 'w', encoding='utf-8') as fw:
json.dump(dic, fw)
print(f'用户{user}抢票成功!')
else:
print(f'用户{user}抢票失败')
def run(user, mutex):
search(user)
# 添加进程互斥锁,让buy()成为一个串行的进程
mutex.acquire()
buy(user)
mutex.release()
# 创建票库文件
def create_data():
with open('data.json', 'w', encoding='utf-8') as fw:
dic = {'ticket_num': 2}
json.dump(dic, fw)
create_data()
if __name__ == '__main__':
mutex = Lock()
for i in range(10):
p = Process(target=run, args=(i, mutex))
p.start()
2、队列
2.1 队列的python实现
取值原则:先进先出
python实现:
模块:from multiprocessing import Queue
使用:
- 实例化队列对象:
q = Queue(max)max 参数为这个队列能存放的数据最大个数 - 取值:按照先进先出原则一个一个的取出
- 1、
q.get()当队列中无数据可取时,程序停在这里,等待队列中进入新的数据 - 2、
q.get_nowait()当队列中无数据可取时,程序不会等待队列中进入新的数据,而是直接报错
- 1、
- 存值:将指定值存进队列,可以打破进程间的数据隔离特性
- 1、
q.put(value)方法,若队列已经存满了,那么使用再存值操作q.put(value)时程序将会停在这里,等待队列空余出位置后,再存值 - 2、
q.put_nowait(value),与q.put(value)不同的是,当队列满了,再使用此方法会报错
- 1、
- 查看队列是否已满:
q.full()返回值为布尔值 - 判断队列是否为空:
q.empty()返回值为布尔值
2.2 队列用于进程间通信
进程间通信:IPC
进程间数据是相互隔离的,若想实现进程间通信,可以利用队列
3、堆栈
取值原则:先进后出
4、生产者与消费者
生产者:生产数据的
消费者:使用数据的
实例:
from multiprocessing import Queue, Process
import time
# 生产者
def producer(name, food, q):
for i in range(10):
data = food
msg = f'用户{name}开始制作{data}'
print(msg)
q.put(data)
time.sleep(0.1)
# 消费者
def consumer(name, q):
while True:
data = q.get()
time.sleep(0.5) # 模拟消费者使用数据时间
print(f'用户{name}在吃{data}')
if __name__ == '__main__':
q = Queue(5)
# 创建生产者,即产生数据的进程对象
p1 = Process(target=producer, args=('tank', '香香的粑粑', q))
p2 = Process(target=producer, args=('nick', '臭臭的粑粑', q))
# 创建消费者,即使用数据的进程对象
c1 = Process(target=consumer, args=('egon', q))
c2 = Process(target=consumer, args=('alex', q))
# 启动进程
p1.start()
p2.start()
# 消费者设置为守护进程
c1.daemon = True
c2.daemon = True
c1.start()
c2.start()
p1.join()
p2.join()
time.sleep(4) # 等待消费者完成任务
5、线程
5.1 什么是线程
线程与进程都是虚拟单位,目的是为了更好的 从队列中获取数据
进程:资源单位
线程:执行单位
开启一个进程,一定会有一个线程,线程才是真正的执行者
5.2 为什么使用线程
开启进程:开辟一个内存空间,占用内存资源,自带一个线程
开启线程:一个进程可以开启多个线程,线程的开销远小于 进程
注意:
- 在python中,同一个进程下的线程不能实现并行,只能实现并发,进程可以实现并行
- 同一进程的线程之间,数据是共享的
5.3 开启线程的两种方式
1、自定义任务
import time
from threading import Thread
def task():
print('线程开启')
time.sleep(1)
print('线程结束')
# t = Thread() # 线程对象可以放在main外面实例化,但是进程对象不行
if __name__ == '__main__':
t = Thread(target=task)
t.start()
2、自定义类
import time
from threading import Thread
class MyThread(Thread):
def run(self):
print('线程开启')
time.sleep(1)
print('线程结束')
t = MyThread()
t.start()
6、线程对象的属性
current_thread().name
t.isAlive()
t.is_alive()
t.getName()获取线程名
t.setName()设置线程名
t.daemon 守护线程,当线程所属进程结束运行,该线程立即结束运行
t.getpid() 获取当前线程所属的进程号
Threadding.enumerate()获取当前进程下所有的线程
from threading import Thread
from threading import current_thread
import time,os
def task():
print(f'线程开启{current_thread().name}')
time.sleep(3)
print(f'线程结束{current_thread().name}')
print(f'当前进程号:{os.getpid()}')
t1 = Thread(target=task)
t2 = Thread(target=task)
# t1.daemon = True
# t2.daemon = True
t1.start()
t2.start()
print(f'当前进程号:{os.getpid()}')
print('主进程代码执行完毕')
'''
线程开启Thread-1
线程开启Thread-2
当前进程号:12132
主进程代码执行完毕
线程结束Thread-2线程结束Thread-1
当前进程号:12132
当前进程号:12132'''
7、线程互斥锁
让并发变成串行,牺牲了执行效率,保证了数据安全
在程序并发执行的情况下,需要修改同一数据时使用
使用方法:
模块:from threading import Lock
使用方法:实例化Lock对象,在线程方法上
from threading import Thread
from threading import Lock
import time
mutex = Lock()
n = 100
def task(i):
print(f'线程{i}启动...')
global n
mutex.acquire()
temp = n
time.sleep(0.1) # 一共等待10秒
n = temp-1
print(n)
mutex.release()
if __name__ == '__main__':
t_l=[]
for i in range(100):
t = Thread(target=task, args=(i, ))
t_l.append(t)
t.start()
for t in t_l:
t.join()
# 100个线程都是在100-1
print(n)

浙公网安备 33010602011771号