The Story That Explains All Queues 🚶सारी Queues समझाने वाली कहानी 🚶一个讲清所有队列的故事 🚶
Each one is a different queue type — and each has a Python version. Picking the right one is the difference between a neat, fast program and a slow, buggy one.
हर एक अलग queue type है — और हर एक का Python version है। सही चुनना ही साफ़-सुथरे, तेज़ program और धीमे, bugs भरे program का फ़र्क़ है।
每一种都是不同的队列类型 — 每一种在 Python 里都有对应的实现。选对了,程序就简洁高效;选错了,程序就又慢又容易出 bug。
A queue is not one thing — it is a family. They all share the same idea (put items in, take items out) but differ in which item comes out next. This tutorial covers the 5 basic queue types and the Python tools you will really use for them.
Queue एक चीज़ नहीं — यह एक परिवार है। सबका विचार एक है (items डालो, items निकालो) पर फ़र्क़ इसमें है कि अगला कौन-सा item निकलेगा। यह tutorial 5 बुनियादी queue types और उनके लिए असल में इस्तेमाल होने वाले Python tools बताता है।
队列不是一种东西,而是一个家族。它们的思路相同(放进元素、取出元素),区别在于 下一个取出的是哪一个。本教程介绍 5 种基本队列类型,以及你在实际中会用到的 Python 工具。
Look for the interactive boxes: the Queue Playground (Section 02), the pop(0) Race (03), the Ring Buffer (07), the Heap Lab (11), the Backpressure Lab (12), the Queue Picker (16) and the Bug Doctor (17). Their results were checked against real Python.
Interactive डिब्बे खोजें: Queue Playground (Section 02), pop(0) Race (03), Ring Buffer (07), Heap Lab (11), Backpressure Lab (12), Queue Picker (16) और Bug Doctor (17)। इनके नतीजे असली Python से जाँचे गए हैं।
请留意这些互动框:队列游乐场(第 02 节)、pop(0) 赛跑(03)、环形缓冲区(07)、堆实验室(11)、背压实验室(12)、队列选择器(16)和 Bug 医生(17)。它们的结果都已与真实的 Python 核对。
The Big Picture — All Queue Types at a Glance 🗺️बड़ी तस्वीर — सारे Queue Types एक नज़र में 🗺️全局视角 — 一眼看懂所有队列类型 🗺️
Five basic patterns. Every Python queue is one of these ideas plus extra machinery (thread safety, blocking, size limits).
पाँच बुनियादी patterns। हर Python queue इन्हीं में से एक विचार है, साथ में अतिरिक्त machinery (thread safety, blocking, size limits)।
五种基本模式。每个 Python 队列都是其中一种思路,再加上额外的机制(线程安全、阻塞、容量限制)。
🎮 Simulator: Queue Playground🎮 Simulator: Queue Playground🎮 模拟器:队列游乐场
Pick a queue type, add and remove items, and watch which item comes out. The code on the left grows with every click. The results were checked against Python on 400 random sequences — including the exact error messages.
एक queue type चुनिए, items जोड़िए और निकालिए, और देखिए कौन-सा item बाहर आता है। हर click के साथ बाईं ओर का code बढ़ता है। नतीजे 400 random sequences पर Python से जाँचे गए — सटीक error messages समेत।
选一种队列类型,添加和移除元素,看看哪个元素先出来。左边的代码会随着每次点击增长。结果已在 400 个随机序列上与 Python 核对 — 包括精确的错误信息。
Type 1 — FIFO Queue (First In, First Out) ☕Type 1 — FIFO Queue (पहले आया, पहले गया) ☕类型 1 — FIFO 队列(先进先出)☕
The default and most natural queue. Items join at the tail and leave from the head, in exactly the order they arrived — the coffee shop line.
सबसे आम और स्वाभाविक queue। Items tail पर जुड़ते हैं और head से निकलते हैं, ठीक उसी क्रम में जिसमें आए — कॉफ़ी की दुकान की कतार।
最常见、最自然的队列。元素从 队尾 进入,从 队头 离开,严格按照到达的顺序 — 就像咖啡店的队伍。
from collections import deque
# deque = the fast Python FIFO (O(1) at both ends)
q = deque()
q.append("A") # enqueue at the tail
q.append("B")
q.append("C")
print(q.popleft()) # "A" - dequeue from the head
print(q.popleft()) # "B"
print(q.popleft()) # "C"
The original said that for 10,000 items list.pop(0) is "10,000× slower" than deque.popleft(). We measured it. Each pop(0) shifts every remaining item, so the gap grows with the size of the list — but at 10,000 items it is closer to 20×, not 10,000×. At 100,000 items it reaches a few hundred times. Real timings:
मूल में कहा गया कि 10,000 items पर list.pop(0), deque.popleft() से "10,000× धीमा" है। हमने नापा। हर pop(0) बाक़ी सारे items खिसकाता है, इसलिए फ़र्क़ list के size के साथ बढ़ता है — पर 10,000 items पर यह लगभग 20× है, 10,000× नहीं। 100,000 items पर यह कुछ सौ गुना तक पहुँचता है। असली timings:
原文说对于 10,000 个元素,list.pop(0) 比 deque.popleft()"慢 10,000 倍"。我们实测了。每次 pop(0) 都要移动剩下的所有元素,所以差距 随列表长度增长 — 但在 10,000 个元素时大约是 20 倍,而不是 10,000 倍。到 100,000 个元素时才达到几百倍。真实计时:
import timeit
for n in (1_000, 10_000, 100_000):
t_list = timeit.timeit("while q: q.pop(0)",
setup=f"q = list(range({n}))", number=1)
t_deque = timeit.timeit("while q: q.popleft()",
setup=f"from collections import deque; q = deque(range({n}))", number=1)
print(f"{n:>7,} items: list.pop(0) {t_list*1000:8.2f} ms | "
f"deque.popleft() {t_deque*1000:6.2f} ms | {t_list/t_deque:5.0f}x slower")
🏁 Simulator: pop(0) Race🏁 Simulator: pop(0) Race🏁 模拟器:pop(0) 赛跑
Watch why pop(0) is slow: after removing the first item, every other item must move one place left. popleft() just moves the start. The bars show real timings for emptying a whole queue (best of 3 runs on our test machine).
देखिए pop(0) धीमा क्यों है: पहला item हटाने के बाद हर दूसरे item को एक जगह बाईं ओर खिसकना पड़ता है। popleft() बस शुरुआत आगे बढ़ाता है। Bars पूरी queue ख़ाली करने की असली timings दिखाते हैं (हमारी test machine पर 3 runs में सबसे अच्छा)।
看看 pop(0) 为什么慢:移除第一个元素后,其余每个元素都要向左挪一位。popleft() 只是移动起点。条形图显示清空整个队列的真实耗时(在我们测试机上 3 次运行中的最好成绩)。
A list lets you write lst.pop(0), but it is O(n): every remaining item shifts down. For small lists you will not notice; for large, busy queues it becomes the slowest part of your program. Use deque or queue.Queue for FIFO.
List में lst.pop(0) लिख सकते हैं, पर यह O(n) है: बाक़ी हर item नीचे खिसकता है। छोटी lists में पता नहीं चलेगा; बड़ी, व्यस्त queues में यह आपके program का सबसे धीमा हिस्सा बन जाता है। FIFO के लिए deque या queue.Queue लीजिए।
列表可以写 lst.pop(0),但它是 O(n) 的:剩下的每个元素都要往前挪。列表小时你察觉不到;队列又大又忙时,它会成为程序里最慢的部分。FIFO 请用 deque 或 queue.Queue。
Type 2 — LIFO Queue (Stack) 🍽️Type 2 — LIFO Queue (Stack) 🍽️类型 2 — LIFO 队列(栈)🍽️
A stack. Items go in and come out at the same end — the top. The last thing you added is the first thing you get back. Perfect for undo history, function calls and depth-first search.
एक stack। Items एक ही सिरे — top — से अंदर जाते और बाहर आते हैं। जो आख़िर में डाला, वही सबसे पहले वापस मिलता है। Undo history, function calls और depth-first search के लिए बढ़िया।
这就是 栈。元素从同一端 — 顶部 — 进出。最后放进去的最先拿回来。非常适合撤销历史、函数调用和深度优先搜索。
# Option A: Python list as a stack (O(1) at the end)
stack = []
stack.append("A") # push
stack.append("B")
stack.append("C")
print(stack.pop()) # "C" - last in, first out
print(stack.pop()) # "B"
# Option B: collections.deque (also O(1); append/pop are thread-safe)
from collections import deque
stack = deque()
stack.append("A")
stack.append("B")
print(stack.pop()) # "B"
# Option C: queue.LifoQueue (thread-safe, blocking API)
from queue import LifoQueue
s = LifoQueue()
s.put("A"); s.put("B"); s.put("C")
print(s.get()) # "C"
Undo/redo history (the last action is undone first), the function-call stack (the last function called returns first), backtracking, depth-first search, matching brackets in a parser, and the browser back button.
Undo/redo history (आख़िरी action सबसे पहले undo), function-call stack (आख़िर में बुलाया function पहले लौटता है), backtracking, depth-first search, parser में brackets मिलाना, और browser का back button।
撤销/重做历史(最后的操作最先撤销)、函数调用栈(最后调用的函数最先返回)、回溯算法、深度优先搜索、解析器中的括号匹配,以及浏览器的后退按钮。
Type 3 — Priority Queue 🏥Type 3 — Priority Queue 🏥类型 3 — 优先队列 🏥
Items do not come out in the order they went in. Each item has a priority, and the most urgent one always comes out first. In Python "most urgent" means the smallest number, because Python's priority queues are min-heaps.
Items उस क्रम में बाहर नहीं आते जिसमें अंदर गए। हर item की एक priority होती है, और सबसे ज़रूरी हमेशा पहले निकलता है। Python में "सबसे ज़रूरी" का मतलब सबसे छोटी संख्या है, क्योंकि Python की priority queues min-heaps हैं।
元素 不会 按进入的顺序出来。每个元素都有一个优先级,最紧急的总是最先出来。在 Python 中,"最紧急"指的是 最小 的数字,因为 Python 的优先队列都是最小堆。
# Option A: heapq - the low-level, fast min-heap
import heapq
pq = []
heapq.heappush(pq, (5, "Change ink"))
heapq.heappush(pq, (1, "Heart attack"))
heapq.heappush(pq, (3, "Cough"))
heapq.heappush(pq, (1, "Stroke"))
heapq.heappush(pq, (2, "Broken arm"))
while pq:
print(heapq.heappop(pq))
# Option B: queue.PriorityQueue - thread-safe, blocking API
from queue import PriorityQueue
pq = PriorityQueue()
pq.put((5, "low urgency"))
pq.put((1, "urgent!"))
print(pq.get()) # (1, 'urgent!')
Heart attack and Stroke both have priority 1. Tuples are compared item by item, so Python then compares the names: "Heart attack" < "Stroke", so Heart attack comes first. That works for strings — but if the second item cannot be compared (a dict, a custom object), you get a TypeError. Section 11 shows this and the fix.
Heart attack और Stroke दोनों की priority 1 है। Tuples की तुलना item दर item होती है, इसलिए Python फिर नामों की तुलना करता है: "Heart attack" < "Stroke", इसलिए Heart attack पहले आता है। Strings के लिए यह चलता है — पर अगर दूसरे item की तुलना न हो सके (dict, custom object), तो TypeError आता है। Section 11 इसे और इसका इलाज दिखाता है।
心脏病发作和中风的优先级都是 1。元组是逐项比较的,所以 Python 接着比较 名称:"Heart attack" < "Stroke",于是心脏病发作排在前面。对字符串这样没问题 — 但如果第二项无法比较(字典、自定义对象),就会得到 TypeError。第 11 节会演示这个问题及修正方法。
Type 4 — Deque (Double-Ended Queue) ↔️Type 4 — Deque (दोनों सिरों वाली Queue) ↔️类型 4 — 双端队列 Deque ↔️
Pronounced "deck". You can add or remove at either end in O(1). A deque can act as a FIFO, a LIFO, or both at once. collections.deque is the most flexible queue in the standard library.
"डेक" बोला जाता है। आप किसी भी सिरे पर O(1) में जोड़ या निकाल सकते हैं। Deque FIFO, LIFO, या दोनों एक साथ बन सकता है। collections.deque standard library की सबसे लचीली queue है।
读作 "deck"。你可以在 任意一端 以 O(1) 的速度添加或移除元素。deque 既能当 FIFO,也能当 LIFO,或者同时兼任。collections.deque 是标准库里最灵活的队列。
from collections import deque
d = deque(["B", "C", "D"])
# Add to either end
d.appendleft("A")
d.append("E")
print(d) # deque(['A', 'B', 'C', 'D', 'E'])
# Remove from either end
print(d.popleft()) # "A"
print(d.pop()) # "E"
print(d) # deque(['B', 'C', 'D'])
# Bounded deque - drops from the OPPOSITE end when full
recent = deque(maxlen=3)
for i in range(6):
recent.append(i)
print(recent) # deque([3, 4, 5], maxlen=3)
# Rotate - a fast circular shift
d = deque([1, 2, 3, 4, 5])
d.rotate(2)
print(d) # deque([4, 5, 1, 2, 3])
d.rotate(-1)
print(d) # deque([5, 1, 2, 3, 4])
deque(maxlen=100) gives you a bounded queue for free — perfect for "last N items" buffers (recent log lines, a moving-average window, recent history). When it is full, adding on one end silently drops an item from the other end.
deque(maxlen=100) आपको मुफ़्त में सीमित queue देता है — "आख़िरी N items" buffers के लिए बढ़िया (हाल की log lines, moving-average window, हाल का इतिहास)। भरने पर एक सिरे पर जोड़ने से दूसरे सिरे का item चुपचाप हट जाता है।
deque(maxlen=100) 免费给你一个有界队列 — 非常适合"最近 N 个元素"的缓冲区(最近的日志行、移动平均窗口、最近的历史记录)。满了之后,在一端添加会悄悄从 另一端 丢掉一个元素。
In the original deque example, lines like d.appendleft("A") # [A, B, C, D] and d.rotate(2) # deque([4, 5, 1, 2, 3]) only had the result in a comment — nothing was printed. We added print() calls so the output above really shows each state.
मूल deque example में d.appendleft("A") # [A, B, C, D] और d.rotate(2) # deque([4, 5, 1, 2, 3]) जैसी lines में नतीजा सिर्फ़ comment में था — कुछ print नहीं होता था। हमने print() जोड़े ताकि ऊपर का output सच में हर स्थिति दिखाए।
原 deque 示例中,像 d.appendleft("A") # [A, B, C, D] 和 d.rotate(2) # deque([4, 5, 1, 2, 3]) 这样的行只是把结果写在注释里 — 实际上什么都没打印。我们加上了 print(),让上面的输出真实显示每一步的状态。
Type 5 — Circular Queue (Ring Buffer) 🔁Type 5 — Circular Queue (Ring Buffer) 🔁类型 5 — 循环队列(环形缓冲区)🔁
A fixed-size queue where the head and tail positions wrap around from the end of the storage back to the start. It never grows and never shrinks — perfect when memory must stay fixed: audio buffers, sensor readings, network packets.
एक तय size की queue जिसमें head और tail की जगहें storage के अंत से वापस शुरुआत पर घूम जाती हैं। यह न बढ़ती है, न घटती है — जब memory तय रखनी हो तब बढ़िया: audio buffers, sensor readings, network packets।
一个固定大小的队列,head 和 tail 的位置会从存储末尾 绕回 开头。它从不增长也从不缩小 — 非常适合内存必须固定的场景:音频缓冲、传感器读数、网络数据包。
Building a circular queue from scratchCircular queue शुरू से बनाना从零实现循环队列
class CircularQueue:
def __init__(self, capacity):
self.buf = [None] * capacity
self.cap = capacity
self.head = 0 # index to read from
self.tail = 0 # index to write to
self.size = 0
def enqueue(self, item):
if self.size == self.cap:
raise OverflowError("Queue full")
self.buf[self.tail] = item
self.tail = (self.tail + 1) % self.cap # wrap around!
self.size += 1
def dequeue(self):
if self.size == 0:
raise IndexError("Queue empty")
item = self.buf[self.head]
self.buf[self.head] = None
self.head = (self.head + 1) % self.cap # wrap around!
self.size -= 1
return item
def __len__(self):
return self.size
# Demo
cq = CircularQueue(4)
for ch in "ABCD":
cq.enqueue(ch)
print(cq.dequeue(), cq.dequeue()) # A B
cq.enqueue("E"); cq.enqueue("F") # tail wraps back to slot 0
print(cq.buf) # ['E', 'F', 'C', 'D']
🔁 Simulator: Ring Buffer🔁 Simulator: Ring Buffer🔁 模拟器:环形缓冲区
Enqueue and dequeue on a ring of 6 slots. Watch head and tail go round and wrap back to slot 0. Then switch to deque(maxlen=6): instead of an error when full, it quietly drops the oldest item. Both were checked against Python on 300 random sequences.
6 slots वाले घेरे पर enqueue और dequeue कीजिए। head और tail को घूमते और slot 0 पर लौटते देखिए। फिर deque(maxlen=6) पर जाइए: भरने पर error की जगह यह चुपचाप सबसे पुराना item हटा देता है। दोनों 300 random sequences पर Python से जाँचे गए।
在一个有 6 个槽位的环上入队和出队。观察 head 和 tail 如何转圈并绕回槽位 0。然后切换到 deque(maxlen=6):满了时它不会报错,而是悄悄丢掉最旧的元素。两者都已在 300 个随机序列上与 Python 核对。
Shortcut — deque(maxlen=…) as a circular bufferछोटा रास्ता — deque(maxlen=…) circular buffer की तरह捷径 — 用 deque(maxlen=…) 作环形缓冲区
from collections import deque
# A ring of the last 5 sensor readings
ring = deque(maxlen=5)
for reading in [10, 11, 13, 12, 14, 15, 16]:
ring.append(reading) # old readings drop off the LEFT automatically
print(ring) # deque([13, 12, 14, 15, 16], maxlen=5)
print(sum(ring) / len(ring)) # rolling average
The two versions behave differently when full. The hand-written CircularQueue refuses new items (OverflowError) — no data is lost. deque(maxlen=N) overwrites the oldest item — always room for the newest. Choose the one that matches what losing data means for you.
भरने पर दोनों versions अलग व्यवहार करते हैं। हाथ से लिखी CircularQueue नए items लेने से मना करती है (OverflowError) — कोई data नहीं खोता। deque(maxlen=N) सबसे पुराने item को ऊपर से लिख देता है — सबसे नए के लिए हमेशा जगह। वह चुनिए जो आपके लिए data खोने के मतलब से मेल खाए।
两个版本在满了的时候表现不同。手写的 CircularQueue 会 拒绝 新元素(OverflowError)— 不丢数据。deque(maxlen=N) 会 覆盖 最旧的元素 — 永远给最新的留位置。根据丢数据对你意味着什么来选择。
Python's queue Module — Thread-Safe Queues 🔒Python का queue Module — Thread-Safe Queues 🔒Python 的 queue 模块 — 线程安全队列 🔒
The types above are ideas. When several threads share a queue, use the queue module: its classes lock for you, and their put()/get() can wait (block) until there is space or an item.
ऊपर के types विचार हैं। जब कई threads एक queue बाँटें, तो queue module लीजिए: इसकी classes आपके लिए lock करती हैं, और इनके put()/get() जगह या item मिलने तक इंतज़ार (block) कर सकते हैं।
上面的类型只是概念。当多个 线程 共享一个队列时,请使用 queue 模块:它的类会替你加锁,put()/get() 还能 等待(阻塞)直到有空位或有元素。
| ClassClass类 | Orderक्रम顺序 | Size limit?Size limit?容量限制? | task_done / join?task_done / join?task_done / join? | When to useकब लें何时使用 |
|---|---|---|---|---|
Queue | FIFO | Yes (maxsize)हाँ (maxsize)有(maxsize) | Yesहाँ有 | The default work queue for threadsThreads के लिए default work queue线程间默认的工作队列 |
LifoQueue | LIFO | Yesहाँ有 | Yesहाँ有 | A stack shared by threadsThreads में बँटा stack线程共享的栈 |
PriorityQueue | Priority (min-heap)Priority (min-heap)优先级(最小堆) | Yesहाँ有 | Yesहाँ有 | Task scheduling by urgencyज़रूरत के हिसाब से task scheduling按紧急程度调度任务 |
SimpleQueue | FIFO | No (unbounded)नहीं (असीमित)无(无界) | Noनहीं无 | Simple, fast hand-offsसरल, तेज़ hand-offs简单、快速的交接 |
from queue import Queue, LifoQueue, PriorityQueue, SimpleQueue
# 1. Standard FIFO Queue
q = Queue(maxsize=10)
q.put("A"); q.put("B"); q.put("C")
print(q.get()) # A
# 2. Stack (LIFO)
s = LifoQueue()
s.put("A"); s.put("B"); s.put("C")
print(s.get()) # C - last in, first out
# 3. Priority (lowest number wins)
p = PriorityQueue()
p.put((3, "low"))
p.put((1, "urgent"))
p.put((2, "med"))
print(p.get()) # (1, 'urgent')
# 4. SimpleQueue - no maxsize, no join(), fastest
sq = SimpleQueue()
sq.put("fast")
print(sq.get()) # fast
How much faster is SimpleQueue?SimpleQueue कितना तेज़ है?SimpleQueue 快多少?
import timeit
for name in ("Queue", "SimpleQueue"):
t = timeit.timeit("for i in r: q.put(i)\nfor i in r: q.get()",
setup=f"import queue; r = range(100_000); q = queue.{name}()",
number=1)
print(f"{name:12s} 100,000 put+get: {t*1000:6.1f} ms")
SimpleQueue is written in C and skips maxsize, task_done() and join(). In our run it was more than 10× faster for plain put/get. Use Queue when you need a size limit or join(); otherwise SimpleQueue is enough.
SimpleQueue C में लिखा है और maxsize, task_done() और join() छोड़ देता है। हमारे run में सादे put/get पर यह 10× से ज़्यादा तेज़ था। Size limit या join() चाहिए तो Queue लीजिए; वरना SimpleQueue काफ़ी है।
SimpleQueue 用 C 实现,省掉了 maxsize、task_done() 和 join()。在我们的运行中,单纯的 put/get 快了 10 倍以上。需要容量限制或 join() 时用 Queue;否则 SimpleQueue 就够了。
Multiprocessing Queues — Between Processes 📫Multiprocessing Queues — Processes के बीच 📫多进程队列 — 进程之间 📫
The queue module works only between threads (one shared memory). Separate processes have separate memory, so use multiprocessing.Queue or JoinableQueue: they pickle each object and send it through a pipe.
queue module सिर्फ़ threads के बीच चलता है (एक साझा memory)। अलग processes की memory अलग होती है, इसलिए multiprocessing.Queue या JoinableQueue लीजिए: ये हर object को pickle करके pipe से भेजते हैं।
queue 模块只能在 线程 之间使用(共享同一块内存)。不同的 进程 内存是分开的,所以要用 multiprocessing.Queue 或 JoinableQueue:它们会把每个对象 pickle 后通过管道发送。
Threads share memory, so a thread queue passes references. Processes do not, so every item is pickled, sent and rebuilt.
Threads memory बाँटते हैं, इसलिए thread queue references भेजती है। Processes नहीं बाँटते, इसलिए हर item pickle, send और फिर से बनाया जाता है।
线程共享内存,所以线程队列传递的是引用。进程不共享,所以每个元素都要被 pickle、发送再重建。
from multiprocessing import Process, JoinableQueue
def worker(q):
while True:
item = q.get()
if item is None:
break
print(f"got {item}", flush=True)
q.task_done() # only on JoinableQueue
if __name__ == "__main__":
q = JoinableQueue() # like Queue + task_done/join
p = Process(target=worker, args=(q,))
p.start()
for i in range(5):
q.put(f"task {i}")
q.join() # wait for all task_done()
q.put(None) # poison pill
p.join()
print("all done")
On Windows and macOS, new processes start by importing your script again. Without if __name__ == "__main__": the child would start more children. Also note flush=True in the worker's print: output from a child process may otherwise arrive late or out of order.
Windows और macOS पर नए processes आपकी script को फिर से import करके शुरू होते हैं। if __name__ == "__main__": के बिना child और children शुरू कर देगा। Worker के print में flush=True भी देखिए: वरना child process का output देर से या ग़लत क्रम में आ सकता है।
在 Windows 和 macOS 上,新进程启动时会重新导入你的脚本。没有 if __name__ == "__main__": 的话,子进程会再启动更多子进程。另外注意 worker 的 print 里有 flush=True:否则子进程的输出可能延迟或乱序。
asyncio.Queue — For async/await Code ⚡asyncio.Queue — async/await Code के लिए ⚡asyncio.Queue — 用于 async/await 代码 ⚡
Inside asyncio code, use asyncio.Queue. It looks like queue.Queue, but put() and get() are coroutines you must await. It is not thread-safe — it is safe between tasks inside one event loop.
asyncio code में asyncio.Queue लीजिए। यह queue.Queue जैसा दिखता है, पर put() और get() coroutines हैं जिन्हें await करना होता है। यह thread-safe नहीं है — यह एक event loop के अंदर tasks के बीच सुरक्षित है।
在 asyncio 代码中,请使用 asyncio.Queue。它看起来像 queue.Queue,但 put() 和 get() 是必须 await 的协程。它 不是线程安全的 — 它只在同一个事件循环内的任务之间是安全的。
import asyncio
async def producer(q, name):
for i in range(3):
await asyncio.sleep(0.1)
await q.put(f"{name}-{i}")
async def consumer(q, name):
while True:
item = await q.get()
if item is None:
break
print(f"[{name}] got {item}")
q.task_done()
async def main():
q = asyncio.Queue(maxsize=10)
prods = [asyncio.create_task(producer(q, f"P{i}")) for i in range(2)]
cons = [asyncio.create_task(consumer(q, f"C{i}")) for i in range(3)]
await asyncio.gather(*prods)
await q.join()
for _ in cons:
await q.put(None)
await asyncio.gather(*cons)
asyncio.run(main())
We made each producer send 3 items instead of 5 so the real output stays short. Notice that items are shared across the three consumers — whichever consumer is free takes the next item.
हमने हर producer से 5 की जगह 3 items भिजवाए ताकि असली output छोटा रहे। ध्यान दीजिए, items तीनों consumers में बँटते हैं — जो consumer ख़ाली हो, अगला item वही लेता है।
我们让每个生产者只发送 3 个元素而不是 5 个,好让真实输出保持简短。注意元素会在三个消费者之间分配 — 哪个消费者空闲,就由它取下一个元素。
A blocking queue.Queue.get() inside async code freezes the whole event loop (see the Bug Doctor). asyncio.Queue used from other threads is not synchronised. multiprocessing.Queue between threads works but is overkill. One rule: pick the queue that matches your concurrency model.
Async code में blocking queue.Queue.get() पूरा event loop जमा देता है (Bug Doctor देखिए)। दूसरे threads से इस्तेमाल किया asyncio.Queue synchronised नहीं होता। Threads के बीच multiprocessing.Queue चलता है पर ज़रूरत से ज़्यादा है। एक नियम: अपने concurrency model से मेल खाती queue चुनिए।
在异步代码中调用会阻塞的 queue.Queue.get() 会冻结整个事件循环(见 Bug 医生)。从其他线程使用 asyncio.Queue 不会正确同步。在线程之间用 multiprocessing.Queue 可以,但杀鸡用牛刀。一条规则:选择与你的并发模型相匹配的队列。
heapq — The Raw Priority-Queue Building Block 🌲heapq — Priority Queue की बुनियादी ईंट 🌲heapq — 优先队列的基础构件 🌲
heapq turns a normal list into a min-heap. PriorityQueue uses it inside, plus locking. With no locking, it is the fastest choice for single-threaded priority work.
heapq एक सामान्य list को min-heap बना देता है। PriorityQueue अंदर से इसी को इस्तेमाल करता है, साथ में locking। बिना locking के यह single-thread priority काम के लिए सबसे तेज़ विकल्प है।
heapq 把普通列表变成 最小堆。PriorityQueue 内部就是用它再加上锁。没有锁的开销,它是单线程优先级任务的最快选择。
import heapq
# A heap is just a plain list, treated with heap operations
h = []
for x in [3, 1, 4, 1, 5]:
heapq.heappush(h, x)
print(heapq.heappop(h)) # 1 (smallest first)
print(heapq.heappop(h)) # 1
print(heapq.heappop(h)) # 3
# Turn an existing list into a heap in O(n)
data = [7, 3, 9, 1, 4, 2]
heapq.heapify(data) # in place
print(data) # [1, 3, 2, 7, 4, 9]
# Top-K largest / smallest in O(n log k)
print(heapq.nlargest(3, data)) # [9, 7, 4]
print(heapq.nsmallest(3, data)) # [1, 2, 3]
After heapify, the list is [1, 3, 2, 7, 4, 9] — not sorted! A heap only promises that each parent is ≤ its children (item i ≤ items 2i+1 and 2i+2). So h[0] is always the smallest, and that is all you need for a priority queue. See it as a tree in the Heap Lab below.
heapify के बाद list [1, 3, 2, 7, 4, 9] है — sorted नहीं! Heap सिर्फ़ यह वादा करता है कि हर parent अपने children से ≤ है (item i ≤ items 2i+1 और 2i+2)। इसलिए h[0] हमेशा सबसे छोटा है, और priority queue के लिए बस इतना ही चाहिए। नीचे Heap Lab में इसे tree के रूप में देखिए।
heapify 之后列表是 [1, 3, 2, 7, 4, 9] — 并没有排序!堆只保证每个父节点 ≤ 它的子节点(第 i 项 ≤ 第 2i+1 和 2i+2 项)。所以 h[0] 永远是最小的,而这正是优先队列所需要的全部。在下面的堆实验室里以树的形式看看。
🌲 Simulator: Heap Lab🌲 Simulator: Heap Lab🌲 模拟器:堆实验室
Push numbers and pop the smallest. Watch a new item bubble up past bigger parents, and after a pop watch the last item move to the top and sink down. The list on the right is exactly what Python's heapq stores — checked on 300 random sequences.
Numbers push कीजिए और सबसे छोटा pop कीजिए। नए item को बड़े parents से ऊपर उठते देखिए, और pop के बाद आख़िरी item को top पर आकर नीचे बैठते देखिए। दाईं ओर की list ठीक वही है जो Python का heapq रखता है — 300 random sequences पर जाँची गई।
压入数字、弹出最小值。观察新元素如何越过更大的父节点 向上冒;弹出后,观察最后一个元素如何移到顶部再 向下沉。右边的列表正是 Python 的 heapq 存储的内容 — 已在 300 个随机序列上核对。
Equal priorities — the TypeError trapबराबर priorities — TypeError का जाल优先级相同 — TypeError 陷阱
import heapq
pq = []
heapq.heappush(pq, (1, {"task": "first urgent"}))
try:
heapq.heappush(pq, (1, {"task": "second urgent"})) # same priority!
except TypeError as e:
print("TypeError:", e)
import heapq, itertools
# Priority queue with a tie-breaker counter
counter = itertools.count()
pq = []
heapq.heappush(pq, (1, next(counter), {"task": "first urgent"}))
heapq.heappush(pq, (1, next(counter), {"task": "second urgent"}))
heapq.heappush(pq, (3, next(counter), {"task": "normal"}))
while pq:
print(heapq.heappop(pq)[2]["task"])
The original tie-breaker example used plain strings as the data. With strings a tie never fails — Python just compares the strings — so the example did not show why the counter matters. It matters when the data cannot be compared, like the dicts above. The counter also keeps equal priorities in arrival order (FIFO), which plain strings do not: they come out alphabetically.
मूल tie-breaker example में data के रूप में सादी strings थीं। Strings के साथ tie कभी fail नहीं होता — Python बस strings की तुलना करता है — इसलिए example यह नहीं दिखाता था कि counter क्यों ज़रूरी है। यह तब ज़रूरी है जब data की तुलना न हो सके, जैसे ऊपर के dicts। Counter बराबर priorities को आने के क्रम (FIFO) में भी रखता है, जो सादी strings नहीं करतीं: वे वर्णमाला के क्रम में निकलती हैं।
原来的平局示例用的是普通字符串作为数据。用字符串时平局永远不会出错 — Python 只是比较字符串 — 所以那个例子没能说明计数器为什么重要。当数据 无法比较 时(比如上面的字典),它才重要。计数器还能让相同优先级的元素保持到达顺序(FIFO),而普通字符串做不到:它们会按字母顺序出来。
Bounded vs Unbounded Queues — Backpressure 🚰सीमित बनाम असीमित Queues — Backpressure 🚰有界 vs 无界队列 — 背压 🚰
If producers add items faster than consumers remove them, an unbounded queue just keeps growing — until memory runs out. A bounded queue (Queue(maxsize=N)) makes put() wait when it is full. The producers slow down to the consumers' speed. This is called backpressure.
अगर producers items को consumers के निकालने से तेज़ डालें, तो असीमित queue बढ़ती ही जाती है — memory ख़त्म होने तक। सीमित queue (Queue(maxsize=N)) भरने पर put() को इंतज़ार कराती है। Producers consumers की गति पर धीमे हो जाते हैं। इसे backpressure कहते हैं।
如果生产者放入元素的速度比消费者取出的速度快,无界 队列就会一直增长 — 直到内存耗尽。有界 队列(Queue(maxsize=N))满了时会让 put() 等待,生产者于是放慢到消费者的速度。这就叫 背压。
🚰 Simulator: Backpressure Lab🚰 Simulator: Backpressure Lab🚰 模拟器:背压实验室
Set how many items the producer adds per tick, how many the consumer removes, and the queue's maxsize. The blue line is the queue length over 30 ticks; red marks show ticks where the producer had to wait. The model was checked against a queue.Queue run with put_nowait() for all 36 settings.
तय कीजिए producer हर tick में कितने items डालता है, consumer कितने निकालता है, और queue का maxsize। नीली line 30 ticks में queue की लंबाई है; लाल निशान वे ticks हैं जहाँ producer को इंतज़ार करना पड़ा। Model को सभी 36 settings के लिए put_nowait() वाले queue.Queue run से जाँचा गया।
设置生产者每个时刻放入多少个元素、消费者取出多少个,以及队列的 maxsize。蓝线是 30 个时刻内的队列长度;红色标记表示生产者不得不等待的时刻。该模型已针对全部 36 种设置,与使用 put_nowait() 的 queue.Queue 运行结果核对。
A maxsize turns the queue into a natural speed limit. When it is full, producers wait, so memory use stays flat no matter how fast work arrives.
maxsize queue को स्वाभाविक speed limit बना देता है। भरने पर producers इंतज़ार करते हैं, इसलिए काम कितना भी तेज़ आए, memory का इस्तेमाल स्थिर रहता है।
maxsize 让队列成为天然的限速器。满了时生产者会等待,所以无论任务来得多快,内存占用都保持平稳。
Real-World Scenarios — Which Queue for What 🌍असली Scenarios — किस काम के लिए कौन-सी Queue 🌍真实场景 — 什么任务用什么队列 🌍
maxsize so memory cannot explode.maxsize दीजिए ताकि memory न फटे।maxsize,防止内存爆炸。deque(maxlen=N) keeps only the last N actions.deque(maxlen=N) सिर्फ़ आख़िरी N actions रखता है।deque(maxlen=N) 只保留最近 N 个操作。PriorityQueue shows the on-call engineer the worst problems first, whenever they arrived.PriorityQueue on-call engineer को सबसे गंभीर समस्याएँ पहले दिखाती है, चाहे वे कभी भी आई हों।PriorityQueue 让值班工程师先看到最严重的问题,不管它们何时到达。JoinableQueue with task_done() and join() tells you when all the work is finished.task_done() और join() वाली JoinableQueue बताती है कि सारा काम कब पूरा हुआ।task_done() 和 join() 的 JoinableQueue 会告诉你所有工作何时完成。asyncio.Queue — never queue.Queue, whose blocking get() would freeze the loop.asyncio.Queue लीजिए — queue.Queue कभी नहीं, जिसका blocking get() loop जमा देगा।asyncio.Queue — 绝不要用 queue.Queue,它会阻塞的 get() 会冻结事件循环。Practical Example — A Multi-Queue Task Scheduler 🗓️काम का Example — Multi-Queue Task Scheduler 🗓️实用示例 — 多队列任务调度器 🗓️
Three worker threads pull from three queues in strict priority order: emergency tasks first, then normal tasks, then background work.
तीन worker threads तीन queues से सख़्त priority क्रम में काम लेते हैं: पहले emergency tasks, फिर normal tasks, फिर background काम।
三个工作线程按严格的优先顺序从 三个队列 中取任务:先紧急任务,再普通任务,最后是后台任务。
from queue import Queue, Empty
import threading, time
emergency = Queue()
normal = Queue()
background = Queue()
def worker():
while True:
# Try each queue in priority order
for name, q in [("emergency", emergency),
("normal", normal),
("bg", background)]:
try:
task = q.get_nowait()
except Empty: # only "this queue is empty"
continue
if task is None:
return
print(f"[{name}] processing {task}")
time.sleep(0.1)
break
else:
time.sleep(0.05) # all empty, wait a bit
# Feed the tasks FIRST, then start the workers
for i in range(10): background.put(f"bg-{i}")
for i in range(5): normal.put(f"task-{i}")
for i in range(2): emergency.put(f"fire-{i}")
threads = [threading.Thread(target=worker) for _ in range(3)]
for t in threads: t.start()
time.sleep(2)
for _ in threads: emergency.put(None) # one poison pill per worker
for t in threads: t.join()
(1) It used a bare except:. That catches every error — a typo, a bug in your task, even Ctrl+C — and silently moves on. We catch only queue.Empty. (2) It started the workers before adding the tasks, so a fast worker could grab a background task before the emergencies arrived. In our runs it happened to work, but the order was luck; we feed the queues first. (3) Its output ended with "…", so it was not a real run. The output above is real.
(1) इसमें खुला except: था। यह हर error पकड़ लेता है — typo, आपके task का bug, यहाँ तक कि Ctrl+C — और चुपचाप आगे बढ़ जाता है। हम सिर्फ़ queue.Empty पकड़ते हैं। (2) इसने tasks जोड़ने से पहले workers शुरू किए, इसलिए कोई तेज़ worker emergencies आने से पहले background task उठा सकता था। हमारे runs में यह चल गया, पर क्रम क़िस्मत से था; हम पहले queues भरते हैं। (3) इसका output "…" पर ख़त्म होता था, यानी असली run नहीं था। ऊपर का output असली है।
(1) 它用了裸露的 except:。这会捕获 所有 错误 — 拼写错误、任务里的 bug,甚至 Ctrl+C — 然后悄悄继续。我们只捕获 queue.Empty。(2) 它在添加任务 之前 就启动了工作线程,所以一个快速的线程可能在紧急任务到来之前抢到后台任务。在我们的运行中碰巧没出问题,但顺序靠的是运气;我们改为先填充队列。(3) 它的输出以"…"结尾,并不是真实运行。上面的输出是真实的。
Complete Comparison Table 📋पूरी तुलना Table 📋完整对比表 📋
| ImplementationImplementation实现 | TypeType类型 | Thread-safeThread-safe线程安全 | Process-safeProcess-safe进程安全 | Blocking APIBlocking API阻塞 API | Speedगति速度 |
|---|---|---|---|---|---|
list | LIFO stack onlyसिर्फ़ LIFO stack仅 LIFO 栈 | Noनहीं否* | Noनहीं否 | Noनहीं否 | Fastestसबसे तेज़最快 |
collections.deque | FIFO / LIFO / Deque | append/pop onlyसिर्फ़ append/pop仅 append/pop | Noनहीं否 | Noनहीं否 | Very fastबहुत तेज़很快 |
heapq | Priority (min-heap)Priority (min-heap)优先级(最小堆) | Noनहीं否 | Noनहीं否 | Noनहीं否 | Very fastबहुत तेज़很快 |
queue.Queue | FIFO | Yesहाँ是 | Noनहीं否 | Yesहाँ是 | Mediumमध्यम中等 |
queue.LifoQueue | LIFO | Yesहाँ是 | Noनहीं否 | Yesहाँ是 | Mediumमध्यम中等 |
queue.PriorityQueue | PriorityPriority优先级 | Yesहाँ是 | Noनहीं否 | Yesहाँ是 | Medium (≈3× slower than heapq here)मध्यम (यहाँ heapq से ≈3× धीमा)中等(此处比 heapq 慢约 3 倍) |
queue.SimpleQueue | FIFO (unbounded)FIFO (असीमित)FIFO(无界) | Yesहाँ是 | Noनहीं否 | Yes (get)हाँ (get)是(get) | Fast (C code)तेज़ (C code)快(C 实现) |
multiprocessing.Queue | FIFO | Yesहाँ是 | Yesहाँ是 | Yesहाँ是 | Slow (pickling)धीमा (pickling)慢(需 pickle) |
multiprocessing.JoinableQueue | FIFO + task_done | Yesहाँ是 | Yesहाँ是 | Yesहाँ是 | Slow (pickling)धीमा (pickling)慢(需 pickle) |
asyncio.Queue | FIFO | Noनहीं否 | Noनहीं否 | Yes (await)हाँ (await)是(await) | Fast (in one loop)तेज़ (एक loop में)快(同一循环内) |
asyncio.LifoQueue | LIFO | Noनहीं否 | Noनहीं否 | Yes (await)हाँ (await)是(await) | Fastतेज़快 |
asyncio.PriorityQueue | PriorityPriority优先级 | Noनहीं否 | Noनहीं否 | Yes (await)हाँ (await)是(await) | Fastतेज़快 |
* A single append() or pop() on a list is atomic in CPython, but a mix of steps from several threads (check, then pop) can still race.
* CPython में list पर एक अकेला append() या pop() atomic है, पर कई threads से कई क़दमों का मेल (पहले check, फिर pop) फिर भी टकरा सकता है।
* 在 CPython 中,对列表的单次 append() 或 pop() 是原子的,但多个线程的多步操作(先检查再弹出)仍可能产生竞争。
The intro promised "8 Python implementations", but this table lists 12. We also checked the speed labels: heapq was about 3× faster than PriorityQueue on our machine (100,000 push+pop), and SimpleQueue more than 10× faster than Queue (Section 08). The labels above now match those measurements.
शुरुआत में "8 Python implementations" का वादा था, पर यह table 12 बताती है। हमने speed labels भी जाँचे: हमारी machine पर heapq, PriorityQueue से लगभग 3× तेज़ था (100,000 push+pop), और SimpleQueue, Queue से 10× से ज़्यादा तेज़ (Section 08)। ऊपर के labels अब इन मापों से मेल खाते हैं।
开头承诺了"8 种 Python 实现",但这张表列出了 12 种。我们还核对了速度标注:在我们的机器上(100,000 次压入 + 弹出),heapq 比 PriorityQueue 快约 3 倍,SimpleQueue 比 Queue 快 10 倍以上(第 08 节)。上表的标注现已与这些测量结果一致。
Which Queue Should I Use? 🧭कौन-सी Queue लूँ? 🧭我该用哪种队列?🧭
Answer three questions and the picker walks the decision for you — the interactive version of the original flowchart.
तीन सवालों के जवाब दीजिए और picker आपके लिए फ़ैसला कर देगा — मूल flowchart का interactive version।
回答三个问题,选择器会替你完成决策 — 这是原流程图的互动版。
Bug Doctor, Golden Rules and Quiz 🏆Bug Doctor, सुनहरे नियम और Quiz 🏆Bug 医生、黄金法则与小测验 🏆
🩺 Simulator: Bug Doctor🩺 Simulator: Bug Doctor🩺 模拟器:Bug 医生
Six real queue mistakes. Buggy code on the left, fixed code on the right, changed lines marked. Every output is from a real run.
Queues की छह असली ग़लतियाँ। बाईं ओर buggy code, दाईं ओर ठीक किया हुआ, बदली lines चिह्नित। हर output असली run से है।
六个真实的队列错误。左边是有 bug 的代码,右边是修正后的代码,改动的行已标出。每个输出都来自真实运行。
list.pop(0) is O(n) and gets slower as the list grows. Use collections.deque or queue.Queue.list.pop(0) O(n) है और list बढ़ने पर धीमा होता जाता है। collections.deque या queue.Queue लीजिए।list.pop(0) 是 O(n) 的,列表越长越慢。请用 collections.deque 或 queue.Queue。append() and pop() at the end are both O(1). For a stack shared by threads, use queue.LifoQueue.append() और pop() दोनों O(1) हैं। Threads में बँटे stack के लिए queue.LifoQueue लीजिए।append() 和 pop() 都是 O(1)。多线程共享的栈请用 queue.LifoQueue。queue module, processes → multiprocessing, asyncio → asyncio.Queue. Do not mix them.queue module, processes → multiprocessing, asyncio → asyncio.Queue। इन्हें मिलाइए मत।queue 模块,进程 → multiprocessing,asyncio → asyncio.Queue。不要混用。maxsize on long-running queues. An unbounded queue grows without limit the moment producers are faster than consumers.maxsize लगाइए। जिस पल producers consumers से तेज़ हों, असीमित queue बिना रुके बढ़ने लगती है।maxsize。一旦生产者比消费者快,无界队列就会无限增长。(priority, next(counter), item) — or use @dataclass(order=True) with field(compare=False) on the data. Otherwise Python tries to compare the items and may raise TypeError.(priority, next(counter), item) — या data पर field(compare=False) के साथ @dataclass(order=True) लीजिए। वरना Python items की तुलना करने की कोशिश करता है और TypeError आ सकता है।(priority, next(counter), item) — 或者使用 @dataclass(order=True) 并在数据字段上设置 field(compare=False)。否则 Python 会尝试比较元素,可能抛出 TypeError。deque(maxlen=N) for any "last N items" job — recent log lines, a rolling average, recent history. It is simpler than a full circular-queue class.deque(maxlen=N) लीजिए — हाल की log lines, rolling average, हाल का इतिहास। यह पूरी circular-queue class से आसान है।deque(maxlen=N) — 最近的日志行、滚动平均、最近的历史记录。它比完整的循环队列类简单。queue.SimpleQueue for simple thread hand-offs. It was more than 10× faster than Queue in our test, because it has no maxsize or task_done()/join().queue.SimpleQueue चुनिए। हमारे test में यह Queue से 10× से ज़्यादा तेज़ था, क्योंकि इसमें maxsize या task_done()/join() नहीं है।queue.SimpleQueue。在我们的测试中它比 Queue 快 10 倍以上,因为它没有 maxsize 和 task_done()/join()。multiprocessing.Queue messages small. Every put()/get() pickles and copies the data. For big arrays, use multiprocessing.shared_memory and send only its name through the queue.multiprocessing.Queue के messages छोटे रखिए। हर put()/get() data को pickle और copy करता है। बड़े arrays के लिए multiprocessing.shared_memory लीजिए और queue से सिर्फ़ उसका नाम भेजिए।multiprocessing.Queue 的消息要小。每次 put()/get() 都会 pickle 并复制数据。大数组请用 multiprocessing.shared_memory,只通过队列传递它的名字。join(), call task_done() once for every get() — in a finally block. Otherwise one exception makes join() wait for ever.join() इस्तेमाल करते हैं, तो हर get() के लिए एक बार task_done() बुलाइए — finally block में। वरना एक exception join() को हमेशा इंतज़ार कराता है।join(),每次 get() 都要在 finally 块里调用一次 task_done()。否则一个异常就会让 join() 永远等下去。None per worker after the real work. It is cleaner than killing threads or processes.None। यह threads या processes को मारने से ज़्यादा साफ़ है।None。这比强行杀死线程或进程更干净。heapq directly. It skips all the locking, and was about 3× faster than PriorityQueue in our test.heapq लीजिए। यह सारी locking छोड़ देता है, और हमारे test में PriorityQueue से लगभग 3× तेज़ था।heapq。它省掉了所有加锁的开销,在我们的测试中比 PriorityQueue 快约 3 倍。Quick Quiz — 8 Questionsछोटा Quiz — 8 सवाल小测验 — 8 道题
First decide which item should come out next: oldest (FIFO → deque), newest (LIFO → list), most urgent (priority → heapq), either end (deque) or only the last N (deque(maxlen=N)). Then decide who shares it: threads → queue, processes → multiprocessing, coroutines → asyncio. Add a maxsize for backpressure, a counter for priority ties, and task_done() in finally when you join().
पहले तय कीजिए अगला कौन-सा item निकले: सबसे पुराना (FIFO → deque), सबसे नया (LIFO → list), सबसे ज़रूरी (priority → heapq), कोई भी सिरा (deque) या सिर्फ़ आख़िरी N (deque(maxlen=N))। फिर तय कीजिए इसे कौन बाँटता है: threads → queue, processes → multiprocessing, coroutines → asyncio। Backpressure के लिए maxsize, priority ties के लिए counter, और join() करते समय finally में task_done() जोड़िए।
先决定 下一个取出哪个元素:最早的(FIFO → deque)、最新的(LIFO → list)、最紧急的(优先级 → heapq)、任意一端(deque)或只保留最近 N 个(deque(maxlen=N))。再决定 谁来共享它:线程 → queue,进程 → multiprocessing,协程 → asyncio。用 maxsize 实现背压,用计数器打破优先级平局,使用 join() 时在 finally 中调用 task_done()。