• 消息队列

  • IPC机制(进程间通信)

  • 生产者消费者模型

  • 线程模型

  • 开设线程的两种方式

  • 线程实现TCP服务端并发

  • 线程join方法

  • 线程间数据共享

  • 守护线程

  • GIL全局解释器锁

 

 

消息队列

"""
队列:先进先出(使用频率很高)
堆栈:先进后出(特定常见下用)
"""
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())  # True
# print(q.get()) # 队列中没有值 继续获取则阻塞等待队列中给值
print(q.get_nowait())  # 队列中如果没有值 直接报错

"""
full() # 判断队列是否满了
empty() # 判断队列中的数据是否取完
get_nowait # 取值 没有值不等待直接报错
上述方法能否在并发的场景下精准使用?
不能用!因为都是判断当前时间队列有无值
之所以介绍队列是因为它可以支持进程间数据通信
"""

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)  # 主进程往队列中存放数据123
   print('主进程')

生产者消费者模型

# 生产者
负责生产/制作数据
 
# 消费者
负责消费/处理数据
 
"""
比如在爬虫领域中
会先通过代码爬取网页数据(爬取网页的代码就可以称之为是生产者)
之后针对网页数据做筛选处理(处理网页的代码就可以称之为消费者)

如果使用进程来演示
除了有至少两个进程之外 还需要一个媒介(消息队列)

以后遇到该模型需要考虑的问题其实就是供需平衡的问题
生产力与消费力要均衡
"""

线程理论

# 什么是线程
进程:资源单位
   线程:执行单位
   进程相当于车间(一个个空间),线程相当于车间里面的流水线(真正干活的)
   '''一个进程中至少有一个线程'''
   """
  进程仅仅是在内存中开辟一块空间(提供线程工作所需的资源)
  线程真正被CPU执行,线程需要的资源跟所在进程的要
  """
# 为什么要有线程
开设线程的消耗远远小于进程
   开进程
  1.申请内存空间
       2.拷贝代码
   开线程
  一个进程内可以开设多个线程 无需申请内存空间、拷贝代码
       一个进程内的多个线程数据是共享的
     
"""
开发一个文本编辑器
获取用户输入并实时展示到屏幕上
并实时保存到硬盘
"""

开设线程的两种方式

"""进程与线程的代码实操几乎是一样"""
1
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=('sd',))
t.start()  # 创建线程的开销非常小 几乎是一瞬间就可以创建
print('主线程')

2
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('sg')
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()

线程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=('sd',))
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
'''同一个进程下,多个线程数据是共享的'''

线程对象属性和方法

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=('傻鸟',))
t2 = Thread(target=task,args=('傻吊',))
t1.daemon = True
t1.start()
t2.start()
print('主线程')

"""
关于daemon:
1.如果某个子线程的daemon属性为False,主线程结束时会检测该子线程是否结束,如果该子线程还在运行,则主线程会等待它完成后再退出;

2.如果某个子线程的daemon属性为True,主线程运行结束时不对这个子线程进行检查而直接退出,同时所有daemon值为True的子线程将随主线程一起结束,而不论是否运行完成。
"""

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的多线程就是垃圾

但要结合实际情况
如果多个任务都是IO密集型的 那么多线程更有优势(消耗的资源更少)
多道技术:切换+保存状态
如果多个任务都是计算机密集型 那么多线程确实没有优势 但是可以用多进程
CPU越多越好
用python可以多进程下面开设多线程从而达到效率最大化
"""
1.所有的解释型语言都无法做到同一进程下多个线程利用多核优势
2.GIL再实际编程中其实不用考虑

 

 posted on 2022-04-20 19:39  鳗鱼的春天  阅读(15)  评论(0编辑  收藏  举报