[Tutor] "Lock"ing threads

Kent Johnson kent37 at tds.net
Mon Aug 29 13:35:05 CEST 2005


Hans Dushanthakumar wrote:
> Hi,
>    In a multithreaded program, how do I ensure that a block of code in a
> thread is always executed fully before passing control to another
> thread. Does "Lock" serve this purpose?

No, not exactly. It is used to prevent threading errors but not in the way you think.

Acquiring a Lock, by itself, doesn't prevent another thread from running. It just prevents another thread from acquiring *the same* lock. So to prevent two bits of code from running "at the same time" you can use a *single* Lock. Change your code to use a global Lock or pass the Lock to the class constructors. 

You will also have to handle waiting on a full Queue; if you just use a shared lock you will deadlock if you put_num() on a full Queue. (put_num.run() will acquire the lock, then it will block waiting for room in the Queue. get_num.run() will not be able to acquire the lock so the Queue will never empty.) Take a look at Queue.py to see one way to handle this.

Note that Queue is already thread-safe for insertion and removal. If you really need to read the size as you do in your code then you need another Lock but the basic Queue does not need this.

You might be interested in "The Little Book of Semaphores" which is an introduction to threading using Python.
http://greenteapress.com/semaphores/

Kent

> 
> The foll program is a dummy one, with 2 threads. One put a number onto a
> queue (of max size 1) and the other thread reads from the que.
> 
> However, on running this program (Win XP, NOT via IDLE - it hangs when I
> run it thru IDLE) the output that I see on screen indicates that the
> block of code within the lock aquire and release was not run completely
> before the other thread started running. Note that the print messages
> from the 2 threads seem to be interspersed together:
> 
> 
> import threading
> import Queue
> 
> class put_num(threading.Thread):
>     stop_thread = 0
> 
>     def __init__(self, num, que):
>         threading.Thread.__init__(self)
>         self.que = que
>         self.num = num
>         self.lock = threading.Lock()
>         
>     def run(self):
>         global stop_thread
>         for k in range (20):
>             self.lock.acquire()
>             print "put_num: ", self.num
>             self.que.put(str(self.num))
>             print "put_num: Que size = ", self.que.qsize()
>             self.num = self.num + 1
>             self.lock.release()
> 
> class get_num(threading.Thread):
>     stop_thread = 0
> 
>     def __init__(self, que):
>         threading.Thread.__init__(self)
>         self.que = que
>         self.lock = threading.Lock()
>         
>     def run(self):
>         global stop_thread
>         for k in range (20):
>             self.lock.acquire()
>             mynum = self.que.get()
>             print "get_num: ", mynum
>             print "get_num: Que size = ", self.que.qsize()
>             self.lock.release()
> 
> my_que = Queue.Queue(1)
> 
> put_num_thread = put_num(742, my_que)
> get_num_thread = get_num(my_que)
> 
> print "Starting threads"
> put_num_thread.start()
> get_num_thread.start()
> 
> print "Waiting for threads to finish"
> put_num_thread.join()
> get_num_thread.join()
> 
> print "Closing down"
> raw_input("\n\nPress enter to Quit: ")
> 
> 
> 
> 
> This is the out put of the above program:
> 
> 
> 
> Starting threads
> put_num:  742
> Waiting for threads to finish
> put_num: Que size =  1
> get_num:  742
> get_num: Que size =  0
> put_num:  743
> put_num: Que size =  1
> get_num:  743
> get_num: Que size =  0
> put_num:  744
> put_num: Que size =  1
> get_num:  744
> get_num: Que size =  0
> put_num:  745
> put_num: Que size =  1
> get_num:  745
> get_num: Que size =  0
> put_num:  746
> put_num: Que size =  1
> get_num:  746
> get_num: Que size =  0
> put_num:  747
> put_num: Que size =  get_num:  747
> get_num: Que size =  0
> 0
> put_num:  748
> put_num: Que size =  1
> get_num:  748
> get_num: Que size =  0
> put_num:  749
> put_num: Que size =  get_num:  749
> get_num: Que size =  0
> 0
> put_num:  750
> put_num: Que size =  1
> get_num:  750
> get_num: Que size =  0
> put_num:  751
> put_num: Que size =  1
> get_num:  751
> get_num: Que size =  0
> put_num:  752
> put_num: Que size =  get_num:  752
> get_num: Que size =  0
> 0
> put_num:  753
> put_num: Que size =  1
> get_num:  753
> get_num: Que size =  0
> put_num:  754
> put_num: Que size =  1
> get_num:  754
> get_num: Que size =  0
> put_num:  755
> put_num: Que size =  get_num:  755
> get_num: Que size =  0
> 0
> put_num:  756
> put_num: Que size =  get_num:  756
> get_num: Que size =  0
> 0
> put_num:  757
> put_num: Que size =  get_num:  757
> get_num: Que size =  0
> 0
> put_num:  758
> put_num: Que size =  1
> get_num:  758
> get_num: Que size =  0
> put_num:  759
> put_num: Que size =  get_num:  759
> get_num: Que size =  0
> 0
> put_num:  760
> put_num: Que size =  1
> get_num:  760
> get_num: Que size =  0
> put_num:  761
> put_num: Que size =  get_num:  761
> get_num: Que size =  0
> 0
> Closing down
> 
> 
> Press enter to Quit:
> _______________________________________________
> Tutor maillist  -  Tutor at python.org
> http://mail.python.org/mailman/listinfo/tutor
> 



More information about the Tutor mailing list