Python Threads

Modified

Overview

Threads allow a program to perform multiple operations simultaneously and non-deterministically. A typical application needing multiple threads of execution is:

  1. one thread handles user input from mouse or keyboard
  2. a second thread is displaying a moving graphic such as a bouncing ball

The following is a quick look at Python threads.

Thread Example

The key point of example1.py is the call of the start() method which:

  1. calls run() method
  2. returns immediately

The effect is the main thread starts one thread running, returns to the for-statement to start another.

 

# example0.py

import time

class example0 :
   def __init__( self, n ):
       self.N=n

    def run(self) :
        print 'Start number: ', self.N
        time.sleep(.01)
        print 'End number: ', self.N

for i in range(5) :
     example0( i ).run();
# example1.py

import threading
import time

class example1(threading.Thread) :
    def __init__( self, n ):
        threading.Thread.__init__(self)
        self.N=n

    def run(self) :
        print 'Start number: ', self.N
        time.sleep(.01)
        print 'End number: ', self.N

for i in range(5) :
     example1( i ).start();
Start number: 0
End number: 0
Start number: 1
End number: 1
Start number: 2
End number: 2
Start number: 3
End number: 3
Start number: 4
End number: 4
Start number: 0
Start number: 1
Start number: End number: 0
2
End number: 1
Start number: 3
End number: 2
Start number: 4
End number:
>>> End number: 3
4

 

Serialization

In example1.py  all threads execute with complete independence allowing for maximum parallelism. When multiple threads can operate on a common resource (e.g. printing to terminal output, changing a bank account balance), mutable access to the resource must be restricted to only a single thread at a time, called serialization.

Python provides a lock that when acquired prevents other threads from acquiring until the lock is released.

The example2.py has added a single, common lock, accessible to 0..4 threads, that ensures the statements executed between acquiring and releasing the lock are performed atomically (i.e. as one unit without other threads executing).

Note that the output of example2.py is almost the same as sequential execution because the threads do nothing else in the run() method. Note that the main thread does not acquire the lock so can execute on parallel with other threads.
 

# example1.py

import threading
import time

class example1(threading.Thread) :
    def __init__( self, n ):
        threading.Thread.__init__(self)
        self.N=n

    def run(self) :
        print 'Start number: ', self.N
        time.sleep(.01)
        print 'End number: ', self.N

for i in range(5) :
     example1( i ).start();
# example2.py

import threading
import time

class example2(threading.Thread) :
   def __init__( self, n ):
      threading.Thread.__init__(self)
      self.N=n

   def run(self) :
      lock.acquire()
      print 'Start number: ', self.N
      time.sleep(.01)
      print 'End number: ', self.N
      lock.release()

lock=threading.Lock()

for i in range(5) :
   example2( i ).start();
Start number: 0
Start number: 1
Start number: End number: 0
2
End number: 1
Start number: 3
End number: 2
Start number: 4
End number:
>>> End number: 3
4
Start number: 0
End number:
>>> 0
Start number: 1
End number: 1
Start number: 2
End number: 2
Start number: 3
End number: 3
Start number: 4
End number: 4