并发编程
子进程回收资源的两种方式:
- join让主进程等待子进程结束,并回收子进程资源,主进程再结束并回收资源。
- 主进程‘正常结束’,子进程与主进程一并被回收资源。
守护进程:
主进程结束时,子进程也必须结束,并回收。
进程间数据是隔离的
进程互斥锁:
互斥锁是一把锁,用来保证数据读写安全的。
抢票列子
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): # buy()
# 网络延时
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):
# 1.假设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()
队列:
先进先出
进--->[3,2,1]---->出[1,2,3]
先存放的数据,就先取出来.
应用:让进程之间数据进行交互.
IPC机制(进程间实现通信)
面试会问:什么是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()
生产者与消费者
生产者:生产数据的
消费者:使用数据的
生产油条的人总比吃油条的人少—>生产数据跟不上,使用数据的人—>供需不平衡
吃油条的人比生产的油条要少—>使用数据的速度跟不上生产数据的 速度
通过队列来实现
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()
# 创建两个生产者
for line in range(10):
p1 = Process(target=producer, args=('tank1', f'Pig饲料{line}', q))
p1.start()
# 创建两个消费者
c1 = Process(target=customer, args=('jason', q))
c2 = Process(target=customer, args=('sean', q))
c1.start()
c2.start()
线程:
什么是线程?
进程:资源单位
线程:执行单位
线程与进程都是虚拟的概念,为了更好表达某种事物.
注意:开启一个进程,一定会自带一个线程,线程才是真正的执行者.
为什么使用线程?
节省资源的占用.
开启进程:
- 会产生一个内存空间,申请一块资源.
- 会自带一个主线程
- 开启子进程的速度要比开启子进程的速度慢
开启线程:
- 一个进程内可以开启的多个线程,从进程的内存中申请执行单位.
- 节省资源
开启三个进程:
- 占用三份内存资源
开启三个线程:
- 从一个内存资源中,申请三个小时执行单位
IO密集型用:多线程
-
IO(时间由用户定):
阻塞:切换+保存状态
计算密集型用:多进程
-
计算(时间由操作系统定)
计算时间很长—>切换+保存状态
注意:进程与进程之间的数据是隔离的,线程与线程之间的 数据是共享的
怎么使用线程?
from threading import Thread
import time
number = 1000
启动线程的方式一:
任务1:
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)
启动线程的方式二:
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()
# t.join()
print(f'主进程(主线程)...{current_thread().name}')
print(number)
线程池
线程池是用来限制 创建的线程数