IPC机制
# 同一台计算机上的多个进程数据是严格意义上的物理隔离(默认)
IPC:进程间通信
# 消息队列:mq、kafka
> 存储数据的地方,所有人都可以存,也都可以取
eg:
from multiprocessing import Queue
'''设置存放数据的个数'''
q = Queue(3)
'''往消息队列里存放数据, 当存放的数据超过设置的数量不会报错,会一直卡在那边'''
q.put(111)
# print(q.full()) # 判断队列是否已满:True or False
q.put(222)
q.put(333)
# print(q.full())
'''取数据, 先进先出, 队列为空不会报错,一直卡在那'''
print(q.get())
# print(q.empty()) # 判断队列是否为空是否是空
print(q.get())
print(q.get())
print(q.get_nowait()) # 队列为空直接报错
''''
full()、empty() 在多进程中都不能使用!!!
'''
# 进程间的通信
from multiprocessing import Process, Queue
def a(q):
q.put('p2添加的数据')
def consumer(q):
print('子进程获取队列中的数据', q.get())
print('子进程获取队列中的数据', q.get())
if __name__ == '__main__':
q = Queue()
# 主进程添加数据
q.put('我是主进程添加数据')
p1 = Process(target=consumer, args=(q,))
p2 = Process(target=a, args=(q,))
p1.start()
p2.start()
print('主')
'''
参数:
q.qsize()查看队列当前大小
q.full()查看队列是否满了
q.empty()查看队列是否为空
q.put('xxx', block=False)数据放不进去就报错
q.put('xxx', timeout=3)规定时间取不到就报错
'''
生产者消费者模型
生产者:
负责产生数据的'人'
消费者:
负责数据处理的'人'
该模型除了有生产者和消费者之外必须还有消息队列
进程对象的多种方法
from multiprocessing import Process, current_process
import os
import time
def task():
print(current_process()) # 对象
print(current_process().pid) # 对象下的pid方法
print('子进程', os.getpid())
print('获取当前进程的主进程号', os.getppid())
if __name__ == '__main__':
print(current_process())
print(current_process().pid)
print('主', os.getpid())
p = Process(target=task)
p.start()
p.terminate() # 终止进程
time.sleep(1)
print(p.is_alive())
<_MainProcess name='MainProcess' parent=None started>
7944
<Process name='Process-1' parent=7944 started>
16236
1. 查看进程的两种方法
python:
current_process().pid
os.getpid()
os.getppid() # 获取主进程id
Windows:
tasklist
2.终止进程
python:
p.terminate()
Windows:
taskill 进程号
3.判断进程是否存活
p.is_alive()
守护进程
# 守护进程会随着守护的进程结束而立即结束
from multiprocessing import Process
import time
def task(name):
print('名字:%s' % name)
time.sleep(3)
print('名字:%s' % name)
if __name__ == '__main__':
p = Process(target=task, args=('xx',))
p.daemon = True
p.start()
print('mmm')
僵尸进程与孤儿进程
僵尸进程
进程结束完毕后并不会立即销毁所有的数据,会有一些信息保留下来(进程号、进程执行时间等)给父进程查看
孤儿进程
子进程正常运行,父进程意外死亡,操作系统会针对孤儿系统会派一个'孤儿院'来管理
互斥锁
# 多进程操作数据很可能会导致数据混乱,所以就诞生了互斥锁
互斥锁:
将并发变成串行,牺牲了效率但是保障了数据的安全
# 实操
锁:建议只加在操作数据的部分,否则整个程序的效率会极低
from multiprocessing import Process, Lock
import json
import random
import time
def search(name):
with open(r'D:\项目文件\网络\data.json', 'r', encoding='utf-8') as f:
data = json.load(f)
print('用户%s正在查票 当前剩余%s' % (name, data.get('ticket_num')))
def buy(name):
with open(r'D:\项目文件\网络\data.json', 'r', encoding='utf-8') as f:
data = json.load(f)
time.sleep(random.randint(1, 3))
if data.get('ticket_num') > 0:
data['ticket_num'] -= 1
with open(r'D:\项目文件\网络\data.json', 'w', encoding='utf-8') as f:
json.dump(data, f)
print('%s买票成功' % name)
else:
print('%s很倒霉 没有抢到票' % name)
def run(name, mutex):
search(name)
mutex.acquire() # 强锁
buy(name)
mutex.release() # 释放锁
if __name__ == '__main__':
mutex = Lock() # 产生一把锁,丢给所有的进程
for i in range(10):
p = Process(target=run, args=('用户%s' % i, mutex))
p.start()
线程
进程
进程其实是资源单位,表示一块内存空间
# 程序运行的过程
线程
线程才是执行单位,表示真正的代码
# 线程才是进程里的实际工作者
1.一个进程内可以开设多个线程
2.同一个进程下的多个线程数据是共享的
3.创建进程与线程的区别
创建进程的消耗要远远大于线程
# 创建线程的两种方式(与建进程的方式一样)
from threading import Thread
import time
def task(name):
print(f'{name} is running')
time.sleep(3)
print(f'{name} is done')
t = Thread(target=task, args=('gjl',))
t.start()
print('主线程')
线程的特性
# 同一个进程下的线程数据是共享的
from threading import Thread
money = 100
def task():
global money
money = 666
print(money)
if __name__ == '__main__':
t = Thread(target=task)
t.start()
t.join()
print(money)
# 查看线程的名字 current_thread
from threading import Thread, current_thread
money = 100
def task():
global money
money = 666
print(current_thread().name)
if __name__ == '__main__':
t = Thread(target=task)
t.start()
t.join()
print(current_thread().name)
# 查看进程下的线程数 active_count
from threading import Thread, current_thread ,active_count
money = 100
def task():
global money
money = 666
print(current_thread().name)
if __name__ == '__main__':
t = Thread(target=task)
t.start()
print(active_count())
t.join()
'''
参数:
is_alive(): 查看是否存活
active_count(): 查看线程数,导入active_count模块
current_thread().name:查看线程名,导入current_thread模块
'''
#记忆
'''
1.python中有GIL锁的原因,确保了在同一个进程下,同一时刻只能有一个线程运行
2.多进程只有在python中用的比较多,其他语言一般使用多线程
3.在CPython中开多线程不能利用多核的优势,只有开对进程才能充分利用,其他语言不存在这个问题
4.8核的CPU,至少起8个线程才能充分利用资源
5.如果不存在GIL锁,起几个线程就能充分利用几个CPU
6.CPython很多模块的都是基于GIL锁写出来的,
7.CPython中io密集使用多线程,计算密集使用多进程
'''
GIL全局解释器锁
'''
1.在CPyhton解释器中存在全局解释器锁简称GIL
2.GIL本质也是一把互斥锁,用来阻止同一个进程内多个线程同时执行
3.GIL的存在是因为CPython解释器中内存管理不是线程安全的(垃圾回收机制)
'''
验证GIL的存在
from threading import Thread
import time
num = 100
def task():
global num
count = num
time.sleep(0.5)
num = count - 1
if __name__ == '__main__':
t_list = []
for i in range(100):
t = Thread(target=task)
t.start()
t_list.append(t)
for t in t_list:
t.join()
print(num)
验证多线程是否有用
需要分情况
情况1
单个CPU
多个CPU
情况2
IO密集型(代码有IO操作)
计算密集型(代码没有IO)
1.单个CPU
IO密集型
多进程
申请额外的空间 消耗更多的资源
多线程
消耗资源相对较少 通过多道技术
ps:多线程有优势!!!
计算密集型
多进程
申请额外的空间 消耗更多的资源(总耗时+申请空间+拷贝代码+切换)
多线程
消耗资源相对较少 通过多道技术(总耗时+切换)
ps:多线程有优势!!!
2.多个CPU
IO密集型
多进程
总耗时(单个进程的耗时+IO+申请空间+拷贝代码)
多线程
总耗时(单个进程的耗时+IO)
ps:多线程有优势!!!
计算密集型
多进程
总耗时(单个进程的耗时)
多线程
总耗时(多个进程的综合)
ps:多进程完胜!!!
from threading import Thread
from multiprocessing import Process
import os
import time
def work():
# 计算密集型
res = 1
for i in range(1, 100000):
res *= i
if __name__ == '__main__':
# print(os.cpu_count()) # 12 查看当前计算机CPU个数
start_time = time.time()
# p_list = []
# for i in range(12): # 一次性创建12个进程
# p = Process(target=work)
# p.start()
# p_list.append(p)
# for p in p_list: # 确保所有的进程全部运行完毕
# p.join()
t_list = []
for i in range(12):
t = Thread(target=work)
t.start()
t_list.append(t)
for t in t_list:
t.join()
print('总耗时:%s' % (time.time() - start_time)) # 获取总的耗时
"""
计算密集型
多进程:5.665567398071289
多线程:30.233906745910645
"""
def work():
time.sleep(2) # 模拟纯IO操作
if __name__ == '__main__':
start_time = time.time()
# t_list = []
# for i in range(100):
# t = Thread(target=work)
# t.start()
# t_list.append(t)
# for t in t_list:
# t.join()
p_list = []
for i in range(100):
p = Process(target=work)
p.start()
p_list.append(p)
for p in p_list:
p.join()
print('总耗时:%s' % (time.time() - start_time))
"""
IO密集型
多线程:0.0149583816528320
多进程:0.6402878761291504
"""
Event事件
# 子进程\子线程之间可以彼此等待彼此
from threading import Thread, Event
import time
event = Event() # 类似于造了一个红灯
def light():
print('红灯')
time.sleep(3)
print('绿灯')
event.set()
def car(name):
print(f'{name}正在等红灯')
event.wait()
print('冲')
if __name__ == '__main__':
t = Thread(target=light)
t.start()
for i in range(10):
t1 = Thread(target=car, args=(i,))
t1.start()
进程池与线程池
# 因为硬件的发展追不上软件,有物理限制,如果我们在编写代码的过程中无限制的创建进程或者线程可能会导致计算机崩溃
from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
import random
import time
import os
# 产生含有固定数量线程的线程池
# pool = ThreadPoolExecutor(20)
pool = ProcessPoolExecutor(5)
def task():
print('task is run')
time.sleep(random.randint(1, 3))
print('task is done', os.getpid())
# 讲任务提交给线程池
if __name__ == '__main__':
for i in range(100):
pool.submit(task)