进程间通讯、线程、GIL全局解释器锁

内置队列

'''
队列:先进先出(使用频率很高)
堆栈:先进后出(特定场景下使用)
'''
# 我们以后会直接使用别人封装好的消息队列 实现各种数据传输
from multiprocessing import Queue
q = Queue(5)  # 自定义队列长度
# 往队列中放数据
q.put(11)
q.put(11)
q.put(11)
print(q.full()) # False 判断队列是否已经满了
q.put(11)
q.put(11)
print(q.full())  # True

print(q.get())
print(q.get())
print(q.empty())  # False 判断队列是否空了
print(q.get())
print(q.get())
print(q.get())
print(q.empty())  # True
print(q.get_nowait())  # 队列中没有值 直接报错
 '''
 full()
 empty()
 get_nowait()
 上述方法不能在并发的场景下精准使用,之所以介绍队列,是因为它可以支持进程间数据通信
 '''

IPC 机制(进程间通信)

'''
1.主进程于子进程数据交互
2.来零个子进程数据交互
本质:不同内存空间中的进程数据交互
'''
from multiprocessing import Process,Queue
def producer(q):
    q.put(11)
    print(f'producer 像队列中放入数据')


def consumer(q):
    data = q.get()
    print(f'consumer 从队列中拿到数据{data}')


if __name__ == "__main__":
    q = Queue()
    p1 = Process(target=producer,args=(q,))
    p2 = Process(target=consumer,args=(q,))
    p2.start()
    p1.start()
    print('主进程')

生产者消费者模型

# 生产者
	负责生成/制作数据
# 消费者
	负责消费/处理数据
    
'''
比如在爬虫领域中
	会先通过代码爬取网页数据(爬取网页数据的代码就可以称之为是生产者)
	之后这对网页数据做筛选处理(处理页面的代码就可以称之为消费者)
	
	如果使用进程来演示	
		出院了有至少两个进程之外 还需要有一个媒介(消息队列)
	之后遇到该模型需要考虑的就是供需平衡 也就是生产力于消费力要均衡
'''
from multiprocessing import Process, Queue,JoinableQueue
import time


def producer(name, food, q):
    for i in range(5):
        q.put(food)
        print(f'{name}制作了一份{food}')
        time.sleep(0.5)


def consumer(name, q):
    while True:
        food = q.get()
        print(f'{name}吃掉了一份{food}')
        time.sleep(1)
        q.task_done()  # 每次去取完数据必须给队列一个反馈

if __name__ == "__main__":
    q = JoinableQueue()
    p1 = Process(target=producer, args=('jason','小炒肉',q))
    p2 = Process(target=producer,args=('alice','宫保鸡丁',q))
    c1 = Process(target=consumer, args=('tom',q,))
    c1.daemon = True
    p2.start()
    c1.start()
    p1.start()
    # 生产者产生完所有数据后 往队列中添加结束的信号
    p1.join()
    p2.join()
    q.join()  # 等待队列中所有数据全部被取走

线程理论

# 什么是线程
进程:资源单位,用来防止程序的容器
线程:执行单位
	进程相当于一个车间,线程相当于车间中的流水线
'''一个进程中最少有一个线程'''
'''
进程仅仅是在内存中开辟一块空间(提供线程工作所需要的资源)
线程真正被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')

# 创建线程无需在__main__下面编写 但是为了统一 还是习惯在子代码中写
t = Thread(target=task, args=('jason', ))
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('jasonNB')
t.start()
print('主线程')

线程实现TCP服务端的并发

from socket import socket
from threading import Thread

server = socket()
server.bind(('127.0.0.1', 8888))
server.listen(5)


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()

线程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=('jason', ))
t.start()
t.join()  # 主线程代码等待子线程代码运行完毕之后再往下执行
print('主线程')
"""
主线程为什么要等着子线程结束才会结束整个进程  
    因为主线程结束也就标志着整个进程的结束 要确保子线程运行过程中所需的各项资源
"""

同一个进程内多个线程数据共享

from threading import Thread
import time

name = 'jason'


def one():
    global name
    name = 111
    print(f'one将name修改为{111}')


def two():
    print(f'two打印name结果为{name}')


t1 = Thread(target=one)
t2 = Thread(target=two)
t1.start()
t1.join()
t2.start()

>>>one将name修改为111
>>>two打印name结果为111

线程对象属性和方法

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=('jason',))
t2 = Thread(target=task, args=('kevin',))
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锁 所以同一个进程下多线程肯定是不能同时运行的 即无法利用多核优势

虽然用一个进程下的多个线程不能利用多核优势 但是可以开设多个进程

如果多个任务都是IO密集型 那么多线程更有优势(消耗的资源更少)
如果多个任务都是计算密集型那么多线程确实没有优势,但是可以使用多进程

以后用python就可以多进程下面开多线程从而达到效率最大化
'''
1.所有的解释性语言都无法做到同一个进程下多个线程利用多核优势
2.GIL在实际编程中其实不需要考虑
posted @   名字只需六字  阅读(28)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示