内容概要
消息队列
IPC机制(进程间通信)
生产者消费者模型
线程理论*
开设线程的两种方法
线程实现TCP服务并发
线程的join方法
线程间的数据共享
守护线程
GIL全局解释器
消息队列
"""
队列: 先进先出(使用的频率较高)
堆栈: 先进后出(在特定的情况下使用)
"""
"可以实现各种数据传输"
from multiprocessing import Queue
q = Queue(5 )
q.put(111 )
q.put(222 )
q.put(333 )
print (q.full())
q.put(444 )
q.put(555 )
print (q.full())
print (q.get())
print (q.get())
print (q.empty())
print (q.get())
print (q.get())
print (q.get())
print (q.empty())
print (q.get())
"""
full()
empty()
get_nowait()
上述方法能否在并发的场景下精准使用???
不能用!!!
之所以介绍队列是因为它可以支持进程间数据通信
"""
IPC机制(进程间通信)
"""
IPC机制可实现:
1.主进程与子进程数据交互
2.两个子进程间数据交互
本质:不同内存空间的进程数据交互
"""
from multiprocessing import Process, Queue
def producer (q ):
print ('子进程producer从队列中取值>>>:' , q.get())
q.put('子进程producer往队列中添加值' )
def consumer (q ):
print ('子进程consumer从队列中取值>>>:' , q.get())
if __name__ == '__main__' :
q = Queue()
p = Process(target=producer, args=(q, ))
p1 = Process(target=consumer, args=(q,))
p.start()
p1.start()
q.put(123 )
print ('主进程' )
生产者消费者模型
负责生产、制作数据
负责消费、处理数据
"""
如在爬虫领域中:
先通过代码爬取网页数据(爬取数据的代码就可以称为生产者)
之后针对网页数据进行筛选处理(处理网页的代码就可以称为消费者)
如使用进程演示:
除了需要至少两个进程外还需要一个媒介(消息队列)
以后遇到该模型需要考虑的问题其实就是供需平衡的问题
生产力与消费力要均衡
"""
from multiprocessing import Process, Queue, JoinableQueue
import time
import random
def producer (name, food, q ):
for i in range (1 ,6 ):
data = f'{name} 生产了{food} {i} '
print (data)
time.sleep(random.randint(1 , 3 ))
q.put(data)
def consumer (name, q ):
while True :
food = q.get()
time.sleep(random.random())
print (f'{name} 吃了{food} ' )
q.task_done()
if __name__ == '__main__' :
q = JoinableQueue()
p1 = Process(target=producer, args=('大厨1' , '韭菜炒蛋' , q))
p2 = Process(target=producer, args=('主厨' , '秘制小汉堡' , q))
c1 = Process(target=consumer, args=('客人1' , q))
c2 = Process(target=consumer, args=('客人2' , q))
c1.daemon = True
c2.daemon = True
p1.start()
p2.start()
c1.start()
c2.start()
p1.join()
p2.join()
"""队列中其实已经自己加了锁 所以多进程取值也不会冲突 并且取走了就没了"""
q.join()
"""执行完上述的join方法表示消费者也已经消费完数据了"""
print ('主线程' )
'''
JoinableQueue 的实例p除了与Queue对象相同的方法之外,还具有以下方法
q.task_done()
使用者使用此方法发出信号,表示q.get()返回的项目已经被处理。
如果调用此方法的次数大于从队列中删除的项目数量,将引发ValueError异常。
q.join()
生产者将使用此方法进行阻塞,直到队列中所有项目均被处理。
阻塞将持续到为队列中的每个项目均调用q.task_done()方法为止。
'''
线程理论*
进程:资源单位
线程:执行单位(真正干活的)
进程相当于车间(一个个空间),线程相当于车间里面的流水线(真正干活的)
'''一个进程中至少有一个线程'''
"""
进程仅仅是在内存中开辟一块空间地址(提供线程工作所需的资源)
线程真正被CPU执行,线程需要的资源跟所在的进程要
"""
开设线程的消耗远远比进程要小的多
开进程:
1. 申请内存空间
2. 拷贝代码
开线程:
一个进程内可以开设多个线程 无需申请内存空间、拷贝代码
一个进程内的多个线程是可以实现数据共享的
"""
开发一个文本编辑器
获取用户输入并实时展示到屏幕上
并实时保存到硬盘中
多种功能应该开设多线程而不是多进程
"""
开设线程的两种方式
"""进程与线程的代码实操几乎是一样的"""
from threading import Thread
import time
def task (name ):
print (f'{name} is running' )
time.sleep(3 )
print (f'{name} is over' )
t = Thread(target=task, args=('task' , ))
t.start()
print ('主线程' )
class MyThread (Thread ):
def __init__ (self, username ):
super ().__init__()
self.username = username
def run (self ):
print (f'{self.username} jason is running' )
time.sleep(3 )
print (f'{self.username} is over' )
t = MyThread('task' )
t.start()
print ('主线程' )
线程实现TCP服务端的并发
服务端
"""仔细体会开设进程和线程的本质区别"""
import socket
from threading import Thread
server = socket.socket()
server.bind(('127.0.0.1' , 8080 ))
server.listen()
def talk (sock ):
while True :
data = sock.recv(1024 )
print (data.decode('utf8' ))
sock.send(data.upper())
while True :
sock, addr = server.accept()
t = Thread(target=talk, args=(sock,))
t.start()
客户端
"""
客户端可以多开几个 模拟并发
"""
import socket
client = socket.socket()
client.connect(('127.0.0.1' , 8080 ))
while True :
client.send(b'hello baby' )
data = client.recv(1024 )
print (data.decode('utf8' ))
线程的join方法
from threading import Thread
import time
def task (name ):
print (f'{name} is running' )
time.sleep(3 )
print (f'{name} is over' )
t = Thread(target=task, args=('task' , ))
t.start()
t.join()
print ('主线程' )
"""
主线程为什么要等着子线程结束才会结束整个进程
因为主线程结束也就标志着整个进程的结束 要确保子线程运行过程中所需的各项资源
"""
同一个进程内的多个线程数据共享
from threading import Thread
money = 10000000000
def task ():
global money
money = 1
t = Thread(target=task)
t.start()
t.join()
print (money)
线程对象属性和方法
1. 验证一个进程下的多个线程是否真的处于一个进程
主线程获取的进程号 与子进程获取的进程号相同
'''验证确实如此 即进程下的多个线程是处于一个进程'''
2. 统计进程下活跃的线程数
active_count()
3. 获取线程的名字
1. current_thread().name
MainThread 主线程
Thread-1 、Thread-2 子线程
2. self.name
守护线程
from threading import Thread
import time
def task (name ):
print (f'{name} is running' )
time.sleep(3 )
print (f'{name} is over' )
t1 = Thread(target=task, args=('task1' ,))
t2 = Thread(target=task, args=('task2' ,))
t1.daemon = True
t1.start()
t2.start()
print ('主线程' )
"""
主线程要等待所有非守护线程结束才可以结束
"""
GIL全局解释器锁
"""纯理论 不影响编程 只不过面试的时候可能会被问到"""
In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly
because CPython’s memory management is not thread-safe. (However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.)
"""
1.回顾
python解释器的类别有很多
Cpython Jpython Ppython
垃圾回收机制
应用计数、标记清除、分代回收
GIL只存在于CPython解释器中,不是python的特征
GIL是一把互斥锁用于阻止同一个进程下的多个线程同时执行
原因是因为CPython解释器中的垃圾回收机制不是线程安全的
反向验证GIL的存在 如果不存在会产生垃圾回收机制与正常线程之间数据错乱
GIL是加在CPython解释器上面的互斥锁
同一个进程下的多个线程要想执行必须先抢GIL锁 所以同一个进程下多个线程肯定不能同时运行 即无法利用多核优势
强调:同一个进程下的多个线程不能同时执行即不能利用多核优势
很多不懂python的程序员会喷python是垃圾 速度太慢 有多核都不能用
反驳:虽然用一个进程下的多个线程不能利用多核优势 但是还可以开设多进程!!!
再次强调:python的多线程就是垃圾!!!
反驳:要结合实际情况
如果多个任务都是IO密集型的 那么多线程更有优势(消耗的资源更少)
多道技术:切换+保存状态
如果多个任务都是计算密集型 那么多线程确实没有优势 但是可以用多进程
CPU越多越好
以后用python就可以多进程下面开设多线程从而达到效率最大化
"""
1. 所有的解释型语言都无法做到同一个进程下多个线程利用多核优势
2. GIL在实际编程中其实不用考虑
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人