Python queue (队列)

摘要:
如果将task_done注释掉主线程就永远阻塞在q.join,不再继续向下执行生产者消费者模型在多线程开发当中,如果生产线程处理速度很快,而消费线程处理速度很慢,那么生产线程就必须等待消费线程处理完,才能继续生产数据。
queue (队列)

主要作用

  1. 解耦,使程序实现松耦合(一个模块修改不会影响其他模块)
  2. 提高效率

队列与列表的关系

队列中数据只有一份,取出就没有了,区别于列表,列表数据取出只是复制了一份

分类

FIFO (先入先出)

queue.Queue(maxsize=0)
示例:

import queue

q = queue.Queue()
q.put(1)
q.put(2)
q.put(3)

print(q.get())
print(q.get())
print(q.get())

输出结果:
1
2
3

LIFO (后入先出)

queue.LifoQueue
示例:

import queue

q = queue.LifoQueue()
q.put(1)
q.put(2)
q.put(3)

print(q.get())
print(q.get())
print(q.get())

输出结果:
3
2
1

PriorityQueue (数据可设置优先级)

queue.PriorityQueue
同优先级的按照 ASCII 排序
示例:

import queue

q = queue.PriorityQueue()
q.put((2, '2'))
q.put((1, '1'))
q.put((3, '3'))
q.put((1, 'a'))

print(q.get())
print(q.get())
print(q.get())
print(q.get())

输出结果:
(1, '1')
(1, 'a')
(2, '2')
(3, '3')

queue 模块

queue 模块中有 Queue 类,LifoQueue、PriorityQueue 都继承了 Queue

maxsize

maxsize 是实例化 Queue 类时的一个参数,默认为 0
Queue(maxsize=0) 可以控制队列中数据的容量

put

Queue.put(block=True, timeout=None)
block 用于设置是否阻塞, timeout 用于设置阻塞时等待时长
put_nowait() = put(block=False)

阻塞

当队列满了之后,put 就会阻塞,一直等待队列不再满时向里面添加数据

不阻塞

当队列满了之后,如果设置 put 不阻塞,或者等待时长到了之后会报错:queue.Full

get

Queue.get(block=True, timeout=None)
get_nowait() = get(block=False)

阻塞

当队列空了之后,get 就会阻塞,一直等待队列中有数据后再获取数据

不阻塞

当队列空了之后,如果设置 get 不阻塞,或者等待时长到了之后会报错:_queue.Empty

full & empty

Queue.empty()/Queue.full() 用于判断队列是否为空、满
尽量使用 qsize 代替

qsize

Queue.qsize() 用于获取队列中大致的数据量
注意:在多线程的情况下不可靠
因为在获取 qsize 时,其他线程可能又对队列进行操作了

join

join 会在队列存在未完成任务时阻塞,等待队列无未完成任务,需要配合 task_done 使用

task_done

执行一次 put 会让未完成任务 +1 ,但是执行 get 并不会让未完成任务 -1 ,需要使用 task_done 让未完成任务 -1 ,否则 join 就无法判断
队列为空时执行会报错:ValueError: task_done() called too many times
示例:

import queue
import threading
import time


def q_put():
    for i in range(10):
        q.put('1')
    while True:
        q.put('2')
        time.sleep(1)


def q_get():
    while True:
        temp = q.get()
        q.task_done()
        print(temp)
        time.sleep(0.3)


q = queue.Queue()
t1 = threading.Thread(target=q_put)
t2 = threading.Thread(target=q_get)
t1.start()
t2.start()
q.join()
print('queue is empty now')

主线程执行到 q.join 就开始阻塞,当 t2 线程将队列中的数据全部取出之后,主线程才继续执行。
如果将 task_done 注释掉主线程就永远阻塞在 q.join,不再继续向下执行

生产者消费者模型(主要用于解耦)

在多线程开发当中,如果生产线程处理速度很快,而消费线程处理速度很慢,那么生产线程就必须等待消费线程处理完,才能继续生产数据。同样的道理,如果消费线程的处理能力大于生产线程,那么消费线程就必须等待生产线程。为了解决这个问题于是引入了生产者和消费者模式
生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。
示例:

import threading
import time
import queue


def producer():
    count = 1
    while 1:
        q.put('No.%i' % count)
        print('Producer put No.%i' % count)
        time.sleep(1)
        count += 1


def customer(name):
    while 1:
        print('%s get %s' % (name, q.get()))
        time.sleep(1.5)


q = queue.Queue(maxsize=5)
p = threading.Thread(target=producer, )
c = threading.Thread(target=customer, args=('jack', ))
p.start()
c.start()

使用生成器也可以实现简单的生产者消费者模型
点击查看 -> 生成器

免责声明:文章转载自《Python queue (队列)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Linux 基本命令(PuTTy 操作)解决nginx使用proxy_pass反向代理时,cookie丢失的问题下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

使用 VisualVM 进行性能分析及调优

VisualVM 是一款免费的集成了多个 JDK 命令行工具的可视化工具,它能为您提供强大的分析能力,对 Java 应用程序做性能分析和调优。这些功能包括生成和分析海量数据、跟踪内存泄漏、监控垃圾回收器、执行内存和 CPU 分析,同时它还支持在 MBeans 上进行浏览和操作。本文主要介绍如何使用 VisualVM 进行性能分析及调优。   概述 开发大...

Java中生成随机数的4种方式!

在 Java 中,生成随机数的场景有很多,所以本文我们就来盘点一下 4 种生成随机数的方式,以及它们之间的区别和每种生成方式所对应的场景。 1.Random Random 类诞生于 JDK 1.0,它产生的随机数是伪随机数,也就是有规则的随机数。Random 使用的随机算法为 linear congruential pseudorandom number...

Java知识体系复习笔记

笔记 收集自其他资料面经等,非原创 Java基础 1. Java程序的运行原理 跨平台性:JVM实现 2. JDK/JRE的区别 JDK ≈ JRE + javacJRE:Java Runtime Environment Java运行环境JDK:Java Development Kit Java开发工具包 JRE的内部有一个Java虚拟机(Java Vi...

《Java2 实用教程(第五版)》学习指导

《Java2 实用教程(第五版)》 第1章Java入门 主要内容:P1 1.1Java的地位:P1 1.2Java的特点:P2 1.3安装JDK:P5 1.4Java程序的开发步骤:P8 1.5简单的Java应用程序:P9 1.6Java反编译:P13 第2章基本数据类型与数组 主要内容:P17 2.1标识符与关键字:...

Java多线程6:synchronized锁定类方法、volatile关键字及其他

同步静态方法 synchronized还可以应用在静态方法上,如果这么写,则代表的是对当前.java文件对应的Class类加锁。看一下例子,注意一下printC()并不是一个静态方法: public classThreadDomain25 { public synchronized static voidprintA() {...

switch_to函数为什么要保存esi/edi/ebx/ebp?

线程切换过程:   interrupt_handler.S(interrupt_handle_entry)  ->  interrupt.c(c_version_handle)  ->  switch_to.S 结论:   在switch_to函数中必须保存esi/edi/edx/ebp这四个寄存器 原因:   GCC在对C、汇编代码进行混合编...