3

Was recently introduced to the queue design in regards to ability to defer processing as well as implementing a "FIFO" etc.

Looked through the documentation in attempt to get a sample queue going to understand how to implement it in my own design / program. But I'm having issues with just running this code:

import queue

def worker():
    while True:
        item = q.get()
        do_work(item)
        q.task_done()

def main():

    q = queue.Queue(maxsize=0)
    for i in range(num_worker_threads):
         t = Thread(target=worker)
         t.daemon = True
         t.start()

    for item in source():
        q.put(item)

    q.join()       # block until all tasks are done

main()

Question: Would like someone to explain what the for loops are doing, I get an error just running the code so I have to be missing something.

Problem Error that occurs: NameError: global name 'num_worker_threads' is not defined

Thank you from a -Python Novice-

MestreLion
  • 12,698
  • 8
  • 66
  • 57
Bain
  • 834
  • 6
  • 11
  • 17
  • 1
    Well, did you define num_worker_threads anywhere? – technillogue Jan 29 '13 at 14:50
  • @Glycan Well i don't know what I'd be defining. Thats what I want to find out. Pretty much how its all used. – Bain Jan 29 '13 at 14:53
  • That error means that you're not defining num_worker_threads, whatever that is. I have no idea what Queue is and generally try to stay away from threads in python, but you still gotta define your variables before using. Look at the docs to see what it's supposed to be. – technillogue Jan 29 '13 at 14:55
  • Threads are in fact useful in python. You just need to know when to use them. As Glycan mentioned, threads will not help you dramatically for processing speed ups because python uses something called a global interpreter lock. Effectively, this means only one thread is running at a given time. However, threads can be very powerful with IO bound processes like reading files. In this case there is downtime that allows processing to continue while data is retrieved from memory. – Paul Seeb Jan 29 '13 at 15:32

2 Answers2

23

The for loop is launching a number of worker threads to perform the function defined by "worker". Here is working code that should run on your system in python 2.7.

import Queue
import threading

# input queue to be processed by many threads
q_in = Queue.Queue(maxsize=0)

# output queue to be processed by one thread
q_out = Queue.Queue(maxsize=0)

# number of worker threads to complete the processing
num_worker_threads = 10

# process that each worker thread will execute until the Queue is empty
def worker():
    while True:
        # get item from queue, do work on it, let queue know processing is done for one item
        item = q_in.get()
        q_out.put(do_work(item))
        q_in.task_done()

# squares a number and returns the number and its square
def do_work(item):
    return (item,item*item)

# another queued thread we will use to print output
def printer():
    while True:
        # get an item processed by worker threads and print the result. Let queue know item has been processed
        item = q_out.get()
        print "%d squared is : %d" % item
        q_out.task_done()

# launch all of our queued processes
def main():
    # Launches a number of worker threads to perform operations using the queue of inputs
    for i in range(num_worker_threads):
         t = threading.Thread(target=worker)
         t.daemon = True
         t.start()

    # launches a single "printer" thread to output the result (makes things neater)
    t = threading.Thread(target=printer)
    t.daemon = True
    t.start()

    # put items on the input queue (numbers to be squared)
    for item in range(10):
        q_in.put(item)

    # wait for two queues to be emptied (and workers to close)   
    q_in.join()       # block until all tasks are done
    q_out.join()

    print "Processing Complete"

main()

Python 3 version per @handle

import queue 
import threading

# input queue to be processed by many threads
q_in = queue.Queue(maxsize=0) 

# output queue to be processed by one thread
q_out = queue.Queue(maxsize=0) 

# number of worker threads to complete the processing
num_worker_threads = 10

# process that each worker thread will execute until the Queue is empty
def worker():
    while True:
        # get item from queue, do work on it, let queue know processing is done for one item
        item = q_in.get()
        q_out.put(do_work(item))
        q_in.task_done()

# squares a number and returns the number and its square
def do_work(item):
    return (item,item*item)

# another queued thread we will use to print output
def printer():
    while True:
        # get an item processed by worker threads and print the result. Let queue know item has been processed
        item = q_out.get()
        print("{0[0]} squared is : {0[1]}".format(item) )
        q_out.task_done()

# launch all of our queued processes
def main():
    # Launches a number of worker threads to perform operations using the queue of inputs
    for i in range(num_worker_threads):
         t = threading.Thread(target=worker)
         t.daemon = True
         t.start()

    # launches a single "printer" thread to output the result (makes things neater)
    t = threading.Thread(target=printer)
    t.daemon = True
    t.start()

    # put items on the input queue (numbers to be squared)
    for item in range(10):
        q_in.put(item)

    # wait for two queues to be emptied (and workers to close)   
    q_in.join()       # block until all tasks are done
    q_out.join()

    print( "Processing Complete" )

main()
Paul Seeb
  • 6,006
  • 3
  • 26
  • 38
  • Here's a patch updating the module name, print and string formatting for Python 3 ``` 1c1 < import Queue --- > import queue 5c5 < q_in = Queue.Queue(maxsize=0) --- > q_in = queue.Queue(maxsize=0) 8c8 < q_out = Queue.Queue(maxsize=0) --- > q_out = queue.Queue(maxsize=0) 30c30 < print "%d squared is : %d" % item --- > print("{0[0]} squared is : {0[1]}".format(item) ) 54c54 < print "Processing Complete" --- > print( "Processing Complete" ) ``` – handle Nov 20 '14 at 16:42
3

You can think of the number of worker threads as the number of bank tellers at a bank. So people (your items) stand in line (your queue) to be processed by a bank teller (your worker thread). Queues are actually an easy and well understood mechanism to manage complexities in threads.

I have adjusted your code a bit to show how it works.

import queue
import time
from threading import Thread

def do_work(item):
    print("processing", item)

def source():
    item = 1
    while True:
        print("starting", item)
        yield item
        time.sleep(0.2)
        item += 1

def worker():
    while True:
        item = q.get()
        do_work(item)
        q.task_done()

q = queue.Queue(maxsize=0)
def main():
    for i in range(2):
        t = Thread(target=worker)
        t.daemon = True
        t.start()

    for item in source():
        q.put(item)

    q.join()       # block until all tasks are done

main()
amigcamel
  • 1,879
  • 1
  • 22
  • 36
Hans Then
  • 10,935
  • 3
  • 32
  • 51