simple threading question

I started to approach multi threading, but I need something confirmed. For my testing purposes, I am using python 2.7.11 on Linux.

I put something really simple together. Two threads start at the same time and count to 9. One starts counting at 1 and the other one starts at 2.

import threading

class myThread(threading.Thread):
    def __init__(self,threadId):
        threading.Thread.__init__(self)
        self.threadId=threadId
    def run(self):
        while self.threadId < 10:
        print self.threadId
        self.threadId+=1

thread1=myThread(1)
thread2=myThread(2)

thread1.start()
thread2.start()

My expectation was line by line counting to 9. The output sometimes looks like this:

1
22

3
34

5
46

7
58

6
9
7
8
9

Apparently, they can write in the same line while the other process is still going on? The line breaker does not always get called properly. Is this as expected?

For testing purposes I changed:

print self.threadId

to

print "cookies", self.threadId

Now the output looks like this:

cookiescookies  12

cookies 3cookies 2

cookies cookies 4
3cookies 5

cookies 4cookies
6cookies 5

cookies 7cookies 6

cookies 8cookies 7

cookies cookies 89

cookies 9

It looks like, we at do not overwrite "cookies". For my next test, I changed:

print "cookies", self.threadId

to

temp="cookies cake", self.threadId
print temp

The output is even more all over the place: (('cookies cake', 'cookies cake', 21))

('cookies cake'('cookies cake', , 3)2
)
('cookies cake'('cookies cake', , 43))

('cookies cake'('cookies cake', , 45))

('cookies cake'('cookies cake', 6)
, 5)
('cookies cake'('cookies c, ake'7)
('cookies cake', 8)
('cookies cake', 9)
, 6)
('cookies cake', 7)
('cookies cake', 8)
('cookies cake', 9)

Is this as expected? Because "duh this is multi threading?"

6 comments · 88% upvoted · other discussions

comments

myegghead

Is this as expected? Because "duh this is multi threading?"

Yes, print is not thread-safe.

baidarkajoe

As far as I know, print is safe in that it's not going to crash or corrupt program state, but it's possible for a print call to occur in the middle of another print call.

A common solution to this problem is to have one thread dedicated to user interaction. The other threads can use a queue to pass items to the dedicated thread.

For example:

WORK_QUEUE = Queue.Queue()      # shared global Queue object

class OutpThread(threading.Thread):            
    def run(self):
        global WORK_QUEUE       
        while True:
            # Queue.get() blocks by default until data is available on the queue - hence the "daemon" option
            output = WORK_QUEUE.get()       
            print output


if __name__ == '__main__':
    outpthread = OutpThread()
    outpthread.daemon = True       # program exits when only daemon threads are left
    outpthread.start()       # outpthread is now waiting for input

Then in your other threads:

   global WORK_QUEUE
   # do some stuff...
   WORK_QUEUE.put("output")

(Note that it would be more flexible to pass the Queue as an argument to __init__ and store it as an attribute of your class instance. That way you don't depend on a global object. This is just an example. The global object is probably ok for a one-off script, anyway.)

One other thing: you should be calling the parent class __init__ method in your subclass. Something like this:

    threading.Thread.__init__(self)

or even

   super(MyThread, self).__init__()

This is so anything normally done by the default __init__ routine is still taken care of. According to the threading module docs, it's required, although the call to run() seems to take care of it.