Threading queue hangs in Python
<p>I am trying to make a parser multi-threaded via a Queue. It seems to work, but my Queue is hanging. I'd appreciate if someone could tell me how to fix this, since I have rarely written multi-threaded code.</p>

<p>This code reads from the Q:</p>

<pre><code>from silk import *
import json
import datetime
import pandas
import Queue
from threading import Thread

l = []
q = Queue.Queue()

def parse_record():
    d = {}
    while not q.empty():
        rec = q.get()
        d['timestamp'] = rec.stime.strftime("%Y-%m-%d %H:%M:%S")
        # ... many ops like this
        d['dport'] = rec.dport
        l.append(d) # l is global
</code></pre>

<p>And this fills the Q:</p>

<pre><code>def parse_records():
    ffile = '/tmp/query.rwf'
    flows = SilkFile(ffile, READ)
    numthreads = 2

    # fill queue
    for rec in flows:
        q.put(rec)
    # work on Queue    
    for i in range(numthreads):
        t = Thread(target = parse_record)
        t.daemon = True
        t.start()

    # blocking
    q.join()

    # never reached    
    data_df = pandas.DataFrame.from_records(l)
    return data_df
</code></pre>

<p>I only call <code>parse_records()</code> in my main. It never terminates.</p>

<p>The <a href="https://docs.python.org/3.6/library/queue.html#queue.Queue.empty" rel="nofollow">Queue.empty doc</a> says:</p>

<blockquote>
  <p>...if empty() returns False it doesnât guarantee that a subsequent call to get() will not block.</p>
</blockquote>

<p>As a minimum you should use <code>get_nowait</code> or risk data loss. But more importantly, the join will only release when all of the queued items have been marked complete with a <a href="https://docs.python.org/3.6/library/queue.html#queue.Queue.task_done" rel="nofollow">Queue.task_done</a>  call:</p>

<blockquote>
  <p>If a join() is currently blocking, it will resume when all items have been processed (meaning that a task_done() call was received for every item that had been put() into the queue).</p>
</blockquote>

<p>As a side note, <code>l.append(d)</code> is not atomic and should be protected with a lock.</p>

<pre><code>from silk import *
import json
import datetime
import pandas
import Queue
from threading import Thread, Lock

l = []
l_lock = Lock()
q = Queue.Queue()

def parse_record():
    d = {}
    while 1:
        try:
            rec = q.getnowait()
            d['timestamp'] = rec.stime.strftime("%Y-%m-%d %H:%M:%S")
            # ... many ops like this
            d['dport'] = rec.dport
            with l_lock():
                l.append(d) # l is global
            q.task_done()
        except Queue.Empty:
            return
</code></pre>

<p>You could shorten your code considerably by using a thread pool from the standard libs.</p>

<pre><code>from silk import *
import json
import datetime
import pandas
import multiprocessing.pool

def parse_record(rec):
    d = {}
    d['timestamp'] = rec.stime.strftime("%Y-%m-%d %H:%M:%S")
    # ... many ops like this
    d['dport'] = rec.dport
    return d

def parse_records():
    ffile = '/tmp/query.rwf'
    flows = SilkFile(ffile, READ)
    pool = multiprocessing.pool.Pool(2)
    data_df = pandas.DataFrame.from_records(pool.map(parse_record), flows)
    pool.close()
    return data_df
</code></pre>

python multithreading queue hang