2019-04-21 11:50:48 +08:00

27 lines
20 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<div class="body" role="main"><div class="section" id="module-queue"><h1><span class="yiyi-st" id="yiyi-10">17.7. <a class="reference internal" href="#module-queue" title="queue: A synchronized queue class."><code class="xref py py-mod docutils literal"><span class="pre">queue</span></code></a> - 同步队列类</span></h1><p><span class="yiyi-st" id="yiyi-11"><strong>源代码:</strong> <a class="reference external" href="https://hg.python.org/cpython/file/3.5/Lib/queue.py">Lib / queue.py</a></span></p><p><span class="yiyi-st" id="yiyi-12"><a class="reference internal" href="#module-queue" title="queue: A synchronized queue class."><code class="xref py py-mod docutils literal"><span class="pre">queue</span></code></a>模块实现多生产者,多消费者队列。</span><span class="yiyi-st" id="yiyi-13">它特别适用于信息必须在多个线程间安全地交换的多线程程序中。</span><span class="yiyi-st" id="yiyi-14">该模块中的<a class="reference internal" href="#queue.Queue" title="queue.Queue"><code class="xref py py-class docutils literal"><span class="pre">Queue</span></code></a>类实现了所有需要的锁定语义。</span><span class="yiyi-st" id="yiyi-15">这取决于Python中线程支持的可用性请参见<a class="reference internal" href="threading.html#module-threading" title="threading: Thread-based parallelism."><code class="xref py py-mod docutils literal"><span class="pre">threading</span></code></a>模块。</span></p><p><span class="yiyi-st" id="yiyi-16">模块实现了三类队列,主要差别在于取得数据的顺序上。</span><span class="yiyi-st" id="yiyi-17">在FIFOFirst In First Out先进先出队列中最早加入的任务会被最先得到。</span><span class="yiyi-st" id="yiyi-18">在LIFOLast In First Out后进先出队列中最后加入的任务会被最先得到就像栈一样</span><span class="yiyi-st" id="yiyi-19">在优先队列中,任务被保持有序(使用<a class="reference internal" href="heapq.html#module-heapq" title="heapq: Heap queue algorithm (a.k.a. priority queue)."><code class="xref py py-mod docutils literal"><span class="pre">heapq</span></code></a>模块),拥有最小值的任务(优先级最高)被最先得到。</span></p><p><span class="yiyi-st" id="yiyi-20"><a class="reference internal" href="#module-queue" title="queue: A synchronized queue class."><code class="xref py py-mod docutils literal"><span class="pre">queue</span></code></a>模块定义了以下类和异常:</span></p><dl class="class"><dt id="queue.Queue"><span class="yiyi-st" id="yiyi-21"> <em class="property">class </em><code class="descclassname">queue.</code><code class="descname">Queue</code><span class="sig-paren">(</span><em>maxsize=0</em><span class="sig-paren">)</span></span></dt><dd><p><span class="yiyi-st" id="yiyi-22">构造一个FIFO队列。</span><span class="yiyi-st" id="yiyi-23"><em>maxsize</em>是个整数,指明了队列中能存放的数据个数的上限。</span><span class="yiyi-st" id="yiyi-24">一旦达到上限,插入会导致阻塞,直到队列中的数据被消费掉。</span><span class="yiyi-st" id="yiyi-25">如果<em>maxsize</em>小于或者等于0队列大小没有限制。</span></p></dd></dl><dl class="class"><dt id="queue.LifoQueue"><span class="yiyi-st" id="yiyi-26"> <em class="property">class </em><code class="descclassname">queue.</code><code class="descname">LifoQueue</code><span class="sig-paren">(</span><em>maxsize=0</em><span class="sig-paren">)</span></span></dt><dd><p><span class="yiyi-st" id="yiyi-27">构造一个LIFO队列。</span><span class="yiyi-st" id="yiyi-28"><em>maxsize</em>是个整数,指明了队列中能存放的数据个数的上限。</span><span class="yiyi-st" id="yiyi-29">一旦达到上限,插入会导致阻塞,直到队列中的数据被消费掉。</span><span class="yiyi-st" id="yiyi-30">如果<em>maxsize</em>小于或者等于0队列大小没有限制。</span></p></dd></dl><dl class="class"><dt id="queue.PriorityQueue"><span class="yiyi-st" id="yiyi-31"> <em class="property">class </em><code class="descclassname">queue.</code><code class="descname">PriorityQueue</code><span class="sig-paren">(</span><em>maxsize=0</em><span class="sig-paren">)</span></span></dt><dd><p><span class="yiyi-st" id="yiyi-32">构造一个优先队列。</span><span class="yiyi-st" id="yiyi-33"><em>maxsize</em>是个整数,指明了队列中能存放的数据个数的上限。</span><span class="yiyi-st" id="yiyi-34">一旦达到上限,插入会导致阻塞,直到队列中的数据被消费掉。</span><span class="yiyi-st" id="yiyi-35">如果<em>maxsize</em>小于或者等于0队列大小没有限制。</span></p><p><span class="yiyi-st" id="yiyi-36">首先检索最低值条目(最低值条目是由<code class="docutils literal"><span class="pre">sorted(list(entries))[0]</span></code>返回的条目。</span><span class="yiyi-st" id="yiyi-37">任务的典型模式就是如<code class="docutils literal"><span class="pre">(priority_number,</span> <span class="pre">data)</span></code>这样的元组。</span></p></dd></dl><dl class="exception"><dt id="queue.Empty"><span class="yiyi-st" id="yiyi-38"> <em class="property">exception </em><code class="descclassname">queue.</code><code class="descname">Empty</code></span></dt><dd><p><span class="yiyi-st" id="yiyi-39">在空的<a class="reference internal" href="#queue.Queue.get" title="queue.Queue.get"><code class="xref py py-meth docutils literal"><span class="pre">get()</span></code></a>对象上调用非阻塞的<a class="reference internal" href="#queue.Queue.get_nowait" title="queue.Queue.get_nowait"><code class="xref py py-meth docutils literal"><span class="pre">get_nowait()</span></code></a>(或者<a class="reference internal" href="#queue.Queue" title="queue.Queue"><code class="xref py py-class docutils literal"><span class="pre">Queue</span></code></a>)会抛出此异常。</span></p></dd></dl><dl class="exception"><dt id="queue.Full"><span class="yiyi-st" id="yiyi-40"> <em class="property">exception </em><code class="descclassname">queue.</code><code class="descname">Full</code></span></dt><dd><p><span class="yiyi-st" id="yiyi-41">在满的<a class="reference internal" href="#queue.Queue.put" title="queue.Queue.put"><code class="xref py py-meth docutils literal"><span class="pre">put()</span></code></a>对象上调用非阻塞的<a class="reference internal" href="#queue.Queue.put_nowait" title="queue.Queue.put_nowait"><code class="xref py py-meth docutils literal"><span class="pre">put_nowait()</span></code></a>(或者<a class="reference internal" href="#queue.Queue" title="queue.Queue"><code class="xref py py-class docutils literal"><span class="pre">Queue</span></code></a>)会抛出此异常。</span></p></dd></dl><div class="section" id="queue-objects"><h2><span class="yiyi-st" id="yiyi-42">17.7.1. </span><span class="yiyi-st" id="yiyi-43">Queue Objects</span></h2><p><span class="yiyi-st" id="yiyi-44">Queue对象<a class="reference internal" href="#queue.Queue" title="queue.Queue"><code class="xref py py-class docutils literal"><span class="pre">Queue</span></code></a><a class="reference internal" href="#queue.LifoQueue" title="queue.LifoQueue"><code class="xref py py-class docutils literal"><span class="pre">LifoQueue</span></code></a><a class="reference internal" href="#queue.PriorityQueue" title="queue.PriorityQueue"><code class="xref py py-class docutils literal"><span class="pre">PriorityQueue</span></code></a>)提供了下述的公共方法。</span></p><dl class="method"><dt id="queue.Queue.qsize"><span class="yiyi-st" id="yiyi-45"> <code class="descclassname">Queue.</code><code class="descname">qsize</code><span class="sig-paren">(</span><span class="sig-paren">)</span></span></dt><dd><p><span class="yiyi-st" id="yiyi-46">返回队列的近似大小。</span><span class="yiyi-st" id="yiyi-47">注意qsize()&gt; 0不保证随后的get()不会阻塞qsize() &lt; maxsize也不会保证put()不会被阻塞。 </span></p></dd></dl><dl class="method"><dt id="queue.Queue.empty"><span class="yiyi-st" id="yiyi-48"> <code class="descclassname">Queue.</code><code class="descname">empty</code><span class="sig-paren">(</span><span class="sig-paren">)</span></span></dt><dd><p><span class="yiyi-st" id="yiyi-49">如果队列为空,返回<code class="docutils literal"><span class="pre">True</span></code>,否则返回<code class="docutils literal"><span class="pre">False</span></code></span><span class="yiyi-st" id="yiyi-50">如果empty()返回<code class="docutils literal"><span class="pre">True</span></code>它不保证后续调用put()不会阻塞。</span><span class="yiyi-st" id="yiyi-51">类似的如果empty()返回<code class="docutils literal"><span class="pre">False</span></code>也不能保证接下来的get()调用不会被阻塞。</span></p></dd></dl><dl class="method"><dt id="queue.Queue.full"><span class="yiyi-st" id="yiyi-52"> <code class="descclassname">Queue.</code><code class="descname">full</code><span class="sig-paren">(</span><span class="sig-paren">)</span></span></dt><dd><p><span class="yiyi-st" id="yiyi-53">如果队列已满,则返回<code class="docutils literal"><span class="pre">True</span></code>,否则返回<code class="docutils literal"><span class="pre">False</span></code></span><span class="yiyi-st" id="yiyi-54">如果full()返回<code class="docutils literal"><span class="pre">True</span></code>它不保证后续调用get()不会阻塞。</span><span class="yiyi-st" id="yiyi-55">类似的如果full()返回<code class="docutils literal"><span class="pre">False</span></code>并不能保证接下来的put()调用不会被阻塞。</span></p></dd></dl><dl class="method"><dt id="queue.Queue.put"><span class="yiyi-st" id="yiyi-56"> <code class="descclassname">Queue.</code><code class="descname">put</code><span class="sig-paren">(</span><em>item</em>, <em>block=True</em>, <em>timeout=None</em><span class="sig-paren">)</span></span></dt><dd><p><span class="yiyi-st" id="yiyi-57"><em>item</em>放入队列中。</span><span class="yiyi-st" id="yiyi-58">如果可选的参数<em>block</em>为真且<em>timeout</em>为空对象(默认的情况,阻塞调用,无超时),如有必要(比如队列满),阻塞调用线程,直到有空闲槽可用。</span><span class="yiyi-st" id="yiyi-59">如果<em>timeout</em>是正数,则它最多阻塞<em>timeout</em>秒,如果在该时间内没有空闲插槽,则引发<a class="reference internal" href="#queue.Full" title="queue.Full"><code class="xref py py-exc docutils literal"><span class="pre">Full</span></code></a>异常。</span><span class="yiyi-st" id="yiyi-60">如果<em>block</em>为假,如果有空闲槽可用将数据放入队列,否则立即抛出<a class="reference internal" href="#queue.Full" title="queue.Full"><code class="xref py py-exc docutils literal"><span class="pre">Full</span></code></a>异常(非阻塞调用,<em>timeout</em>被忽略)。</span></p></dd></dl><dl class="method"><dt id="queue.Queue.put_nowait"><span class="yiyi-st" id="yiyi-61"> <code class="descclassname">Queue.</code><code class="descname">put_nowait</code><span class="sig-paren">(</span><em>item</em><span class="sig-paren">)</span></span></dt><dd><p><span class="yiyi-st" id="yiyi-62">等同于<code class="docutils literal"><span class="pre">put(item,</span> <span class="pre">False)</span></code>(非阻塞调用)。</span></p></dd></dl><dl class="method"><dt id="queue.Queue.get"><span class="yiyi-st" id="yiyi-63"> <code class="descclassname">Queue.</code><code class="descname">get</code><span class="sig-paren">(</span><em>block=True</em>, <em>timeout=None</em><span class="sig-paren">)</span></span></dt><dd><p><span class="yiyi-st" id="yiyi-64">从队列中移除并返回一个数据。</span><span class="yiyi-st" id="yiyi-65">如果可选的参数<em>block</em>为真且<em>timeout</em>为空对象(默认的情况,阻塞调用,无超时),阻塞调用进程直到有数据可用。</span><span class="yiyi-st" id="yiyi-66">如果<em>超时</em>是正数,则它最多阻塞<em>超时</em>秒,如果在该时间内没有可用的项,则引发<a class="reference internal" href="#queue.Empty" title="queue.Empty"><code class="xref py py-exc docutils literal"><span class="pre">Empty</span></code></a>异常。</span><span class="yiyi-st" id="yiyi-67">如果<em>block</em>为假,如果有数据可用返回数据,否则立即抛出<a class="reference internal" href="#queue.Empty" title="queue.Empty"><code class="xref py py-exc docutils literal"><span class="pre">Empty</span></code></a>异常(非阻塞调用,<em>timeout</em>被忽略)。</span></p></dd></dl><dl class="method"><dt id="queue.Queue.get_nowait"><span class="yiyi-st" id="yiyi-68"> <code class="descclassname">Queue.</code><code class="descname">get_nowait</code><span class="sig-paren">(</span><span class="sig-paren">)</span></span></dt><dd><p><span class="yiyi-st" id="yiyi-69">等同于<code class="docutils literal"><span class="pre">get(False)</span></code>(非阻塞调用)。</span></p></dd></dl><p><span class="yiyi-st" id="yiyi-70">提供了两种方法来支持跟踪入队任务是否已完全由守护进程消费者线程处理。 </span></p><dl class="method"><dt id="queue.Queue.task_done"><span class="yiyi-st" id="yiyi-71"> <code class="descclassname">Queue.</code><code class="descname">task_done</code><span class="sig-paren">(</span><span class="sig-paren">)</span></span></dt><dd><p><span class="yiyi-st" id="yiyi-72">意味着之前入队的一个任务已经完成。</span><span class="yiyi-st" id="yiyi-73">由队列的消费者线程调用。</span><span class="yiyi-st" id="yiyi-74">每一个<a class="reference internal" href="#queue.Queue.get" title="queue.Queue.get"><code class="xref py py-meth docutils literal"><span class="pre">get()</span></code></a>调用得到一个任务,接下来的<a class="reference internal" href="#queue.Queue.task_done" title="queue.Queue.task_done"><code class="xref py py-meth docutils literal"><span class="pre">task_done()</span></code></a>调用告诉队列该任务已经处理完毕。</span></p><p><span class="yiyi-st" id="yiyi-75">If a <a class="reference internal" href="#queue.Queue.join" title="queue.Queue.join"><code class="xref py py-meth docutils literal"><span class="pre">join()</span></code></a> is currently blocking, it will resume when all items have been processed (meaning that a <a class="reference internal" href="#queue.Queue.task_done" title="queue.Queue.task_done"><code class="xref py py-meth docutils literal"><span class="pre">task_done()</span></code></a> call was received for every item that had been <a class="reference internal" href="#queue.Queue.put" title="queue.Queue.put"><code class="xref py py-meth docutils literal"><span class="pre">put()</span></code></a> into the queue).</span></p><p><span class="yiyi-st" id="yiyi-76">如果该方法被调用的次数多于被放入队列中的任务的个数,<a class="reference internal" href="exceptions.html#ValueError" title="ValueError"><code class="xref py py-exc docutils literal"><span class="pre">ValueError</span></code></a>异常会被抛出。</span></p></dd></dl><dl class="method"><dt id="queue.Queue.join"><span class="yiyi-st" id="yiyi-77"> <code class="descclassname">Queue.</code><code class="descname">join</code><span class="sig-paren">(</span><span class="sig-paren">)</span></span></dt><dd><p><span class="yiyi-st" id="yiyi-78">阻塞调用线程,直到队列中的所有任务被处理掉。</span></p><p><span class="yiyi-st" id="yiyi-79">只要有数据被加入队列,未完成的任务数就会增加。</span><span class="yiyi-st" id="yiyi-80">当消费者线程调用<a class="reference internal" href="#queue.Queue.task_done" title="queue.Queue.task_done"><code class="xref py py-meth docutils literal"><span class="pre">task_done()</span></code></a>以指示该项目已检索并且其上的所有工作都已完成时,计数将减少。</span><span class="yiyi-st" id="yiyi-81">当未完成的任务数降到0<a class="reference internal" href="#queue.Queue.join" title="queue.Queue.join"><code class="xref py py-meth docutils literal"><span class="pre">join()</span></code></a>解除阻塞。</span></p></dd></dl><p><span class="yiyi-st" id="yiyi-82">等待入队任务被怎样完成的例子:</span></p><pre><code class="language-python"><span></span><span class="k">def</span> <span class="nf">worker</span><span class="p">():</span>
<span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
<span class="n">item</span> <span class="o">=</span> <span class="n">q</span><span class="o">.</span><span class="n">get</span><span class="p">()</span>
<span class="k">if</span> <span class="n">item</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">break</span>
<span class="n">do_work</span><span class="p">(</span><span class="n">item</span><span class="p">)</span>
<span class="n">q</span><span class="o">.</span><span class="n">task_done</span><span class="p">()</span>
<span class="n">q</span> <span class="o">=</span> <span class="n">queue</span><span class="o">.</span><span class="n">Queue</span><span class="p">()</span>
<span class="n">threads</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">num_worker_threads</span><span class="p">):</span>
<span class="n">t</span> <span class="o">=</span> <span class="n">threading</span><span class="o">.</span><span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">worker</span><span class="p">)</span>
<span class="n">t</span><span class="o">.</span><span class="n">start</span><span class="p">()</span>
<span class="n">threads</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">t</span><span class="p">)</span>
<span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">source</span><span class="p">():</span>
<span class="n">q</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">item</span><span class="p">)</span>
<span class="c1"># block until all tasks are done</span>
<span class="n">q</span><span class="o">.</span><span class="n">join</span><span class="p">()</span>
<span class="c1"># stop workers</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">num_worker_threads</span><span class="p">):</span>
<span class="n">q</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="kc">None</span><span class="p">)</span>
<span class="k">for</span> <span class="n">t</span> <span class="ow">in</span> <span class="n">threads</span><span class="p">:</span>
<span class="n">t</span><span class="o">.</span><span class="n">join</span><span class="p">()</span>
</code></pre><div class="admonition seealso"><p class="first admonition-title"><span class="yiyi-st" id="yiyi-83">参见</span></p><dl class="docutils"><dt><span class="yiyi-st" id="yiyi-84"><a class="reference internal" href="multiprocessing.html#multiprocessing.Queue" title="multiprocessing.Queue"><code class="xref py py-class docutils literal"><span class="pre">multiprocessing.Queue</span></code></a></span></dt><dd><span class="yiyi-st" id="yiyi-85">用于在多进程(而不是多线程)上下文中使用的队列类。</span></dd></dl><p class="last"><span class="yiyi-st" id="yiyi-86"><a class="reference internal" href="collections.html#collections.deque" title="collections.deque"><code class="xref py py-class docutils literal"><span class="pre">collections.deque</span></code></a>是不需要锁定的快速原子<a class="reference internal" href="collections.html#collections.deque.append" title="collections.deque.append"><code class="xref py py-meth docutils literal"><span class="pre">append()</span></code></a><a class="reference internal" href="collections.html#collections.deque.popleft" title="collections.deque.popleft"><code class="xref py py-meth docutils literal"><span class="pre">popleft()</span></code></a>操作的无界队列的替代实现。</span></p></div></div></div></div>