Loading

消息队列、ipc机制、生产者消费者模型、线程理论、守护线程、GIL全局解释器锁

舔狗日记:当了她三年的舔狗,昨天终于追到了女神,可是在追到的那一刻,突然觉得索然无味。原来我并没有真的喜欢她,只是单纯的喜欢当舔狗而已。_索然无味_喜欢_追到表情

消息队列

内置队列Queue

队列:先进先出(使用频率很高)
堆栈:先进后出(特定情况下用)
# multiprocessing 模块里的 Queue 来创建队列
from multiprocessing import Queue

q = Queue(5)  # 自定义队列的长度
q.put(111)
q.put(222)
q.put(333)
print(q.full())  # False  判断队列是否满了
q.put(444)
q.put(555)
print(q.full())  # True
# q.put(666)  # 超出队列的长度,原地阻塞等待队列中出现空位
print(q.get())
print(q.get())
print(q.empty())  # False  判断队列是否空了
print(q.get())
print(q.get())
print(q.get())
print(q.empty())
# print(q.get())  # 队列中没有值 继续获取则阻塞等待队列中给值
print(q.get_nowait())  # 队列中如果没有值 直接报错
'''
full()
empty()
get_nowait()
不能再并发的场景下使用

但队列可以支持进程间数据通信

'''

IPC机制(进程间通信)

'''
用于主进程与子进程数据交互
两个子进程数据交互
本质:不同内存空间中的进程数据交互
'''
# eg:
from multiprocessing import Process, Queue


def producer(q):
    q.put('子进程producer往队列中添加值>>>1')


def consumer(q):
    print('子进程consumer从队列中取值', q.get())


if __name__ == '__main__':
    q = Queue()
    p1 = Process(target=producer, args=(q,))
    p2 = Process(target=consumer, args=(q,))
    p1.start()
    p2.start()
    # q.put(123)  # 主进程往队列中存放数据
    print('主进程')

生产者消费者模型

# 生产者:负责生产/制作数据
# 消费者:负责消费/处理数据
# 为什么要使用生产者消费者模型?
'''
在并发编程中,如果生产者处理速度很快,而消费者处理速度比较慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个等待的问题,就引入了生产者与消费者模型。让它们之间可以不停的生产和消费。
'''
from multiprocessing import Process, JoinableQueue
import time
import random


def producer(name, food, q):
    for i in range(5):
        data = f'{name}生产了{food}{i}'
        print(data)
        time.sleep(random.randint(1, 2))
        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=('大厨jason', '韭菜炒蛋', q))
    p2 = Process(target=producer, args=('老板kevin', '秘制小汉堡', q))
    c1 = Process(target=consumer, args=('客人tuzi', q))
    c2 = Process(target=consumer, args=('客人tony', q))
    c1.daemon = True
    c2.daemon = True
    p1.start()
    p2.start()
    c1.start()
    c2.start()
    # 生产者生产完所有数据之后 往队列中添加结束的信号
    p1.join()
    p2.join()
    q.join() # 等待队列中数据全部被取出(一定要让生产者全部结束才能判断正确)
 """队列中其实已经自己加了锁 所以多进程取值也不会冲突 并且取走了就没了"""
"""执行完上述的join方法表示消费者也已经消费完数据了"""

舔狗日记今天晚上也好冷,本来以为街上没人,结果刚刚偷电动车的時候被抓了,本来想反抗,警察说了一句老实点別动,我立刻就放弃了抵抗,因为我记得你说过,你喜欢老实人。 - 舔狗日记表情包

线程理论

# 什么是线程
进程:资源单位
线程:执行单位
进程相当于车间(一个个空间),线程相当于车间里面的流水线(真正干活的)
'''一个进程中至少有一个线程'''
"""
进程仅仅是在内存中开辟一块空间(提供线程工作所需的资源)
线程真正被CPU执行,线程需要的资源跟所在进程的要
"""

为什么要有线程

# 开设线程的消耗远远小于进程
# 开进程
	1.申请内存空间
    2.拷贝代码
# 开线程
	一个进程内可以开多个线程、不用申请内存空间、拷贝代码
    一个进程内的多个线程数据是共享的
# 比如:
'''
开发一个文本编辑器
	获取用户输入并实时展示到屏幕上
	实时保存到硬盘中
多种功能应该开设多线程而不是多进程
'''

开设线程的两种方式

"""进程与线程的代码实操几乎是一样的"""
from threading import Thread
import time

# 函数的方式
# def task(name):
#     print(f'{name} is running')
#     time.sleep(2)
#     print(f'{name} is over')
# 创建线程无需在__main__下面编写 但是为了统一 还是习惯在子代码中写
# t = Thread(target=task,args=('tuzi',))
# t.start()  # 创建线程的开销极小 几乎是一瞬间就可以创建
# print('主线程') 

# 类创建线程的方式
class MyThread(Thread):
    def __init__(self, username):
        super().__init__()
        self.username = username

    def run(self):
        print(f'{self.username} is running')
        time.sleep(3)
        print(f'{self.username} is over')


t = MyThread('tuzi')
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(b'hello my baby')


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:
    msg = input('输入消息>>>:').strip()
    if len(msg) == 0:
        continue
    client.send(msg.encode('utf8'))
    data = client.recv(1024)
    print(data.decode('utf8'))

线程join方法

"""
主线程为什么要等着子线程结束才会结束整个进程  
    因为主线程结束也就标志着整个进程的结束 要确保子线程运行过程中所需的各项资源
"""
# join 主线程代码等待子线程代码运行完毕之后再往下执行
from threading import Thread
import time


def task(name):
    print(f'{name} is running')
    time.sleep(1)
    print(f'{name} is over')


t = Thread(target=task, args=('tuzi',))
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.验证一个进程下的多个线程是否真的处于一个进程
import time
from threading import Thread,active_count,current_thread,main_thread
import os

def task():
    time.sleep(1)
    print('子线程号:',os.getpid())
    print(current_thread().name)  # Thread-1  子线程名字
t = Thread(target=task)
t.start()
print(active_count())
print('主线程号:',os.getpid())
print(main_thread().name)  # MainThread   主线程名字
2.统计进程下活跃的线程数
	active_count()  # 注意主线程也算
3.获取线程的名字
	current_thread().name  # 子线程名字
    main_thread().name  # 主线程名字
 # self.name  # 代码如下
class MyThread(Thread):
def run(self):
	print(self.name)  # Thread-1
t = MyThread()
t.start()

舔狗日记今天表白被拒,她对我说能不能脱下裤子撒泡尿照照自己,当我脱下裤子的时候,她吞了一下口水,说我们可以试一试。 - 舔狗日记表情包

守护线程

主线程结束子线程立马结束

from threading import Thread
import time


def task(name):
    print(f'{name} is running')
    time.sleep(2)
    print(f'{name} is over')


t1 = Thread(target=task, args=('tuzi',))
t2 = Thread(target=task, args=('jason',))
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在实际编程中其实不用考虑

50f970c802e59011962f424c01177ab

舔狗反击日记你以为我是备胎,你错了,只要我舔的女神够多,你就是我的备胎 - 舔狗反击日记表情包

posted @ 2022-04-20 21:28  香菜根  阅读(28)  评论(0编辑  收藏  举报