52
2002 Prentice Hall. All rights reserved. Chapter 19 - Multithreading Outline 19.1 Introduction 19.2 Thread States: Life Cycle of a Thread 19.3 threading.Thread Example 19.4 Thread Synchronization 19.5 Producer/Consumer Relationship without Thread Synchronization 19.6 Producer/Consumer Relationship with Thread Synchronization 19.7 Producer/Consumer Relationship: Module Queue 19.8 Producer/Consumer Relationship: The Circular Buffer 19.9 Semaphores 19.10 Events

2002 Prentice Hall. All rights reserved. Chapter 19 - Multithreading Outline 19.1 Introduction 19.2 Thread States: Life Cycle of a Thread 19.3 threading.Thread

Embed Size (px)

Citation preview

2002 Prentice Hall. All rights reserved.

Chapter 19 - MultithreadingOutline19.1 Introduction19.2 Thread States: Life Cycle of a Thread19.3 threading.Thread Example19.4 Thread Synchronization19.5 Producer/Consumer Relationship without Thread Synchronization19.6 Producer/Consumer Relationship with Thread Synchronization19.7 Producer/Consumer Relationship: Module Queue19.8 Producer/Consumer Relationship: The Circular Buffer19.9 Semaphores19.10 Events

2002 Prentice Hall. All rights reserved.

19.1 Introduction

• Threads often called “light-weight” process because operating systems generally require fewer resources to create and manage threads than to create and manage processes

• Python’s threading capabilities depend on whether the operating system supports multithreading

• Python’s threading module provides its multithreading capabilities

2002 Prentice Hall. All rights reserved.

19.2 Thread States: Life Cycle of a Thread

• Python interpreter controls all program threads• Python’s global interpreter lock (GIL) ensures that

the interpreter runs only one thread at any given time

• Thread is said to be in one of several thread states at any time

• Python programs can define threads by inheriting from class threading.Thread and overriding its functionality

2002 Prentice Hall. All rights reserved.

19.2 Thread States: Life Cycle of a Thread

• Newly created thread begins its lifecycle in the born state

• Invoking the thread’s start method places the thread in the ready state

• The thread’s run method, which implements the tasks that the thread performs, obtains the GIL and enters the running state

• Thread enters the dead state when its run method returns or terminates for any reason (e.g., an uncaught exception)

2002 Prentice Hall. All rights reserved.

19.2 Thread States: Life Cycle of a Thread

• A running thread that calls another thread’s join method forfeits the GIL and waits for the joined thread to die before proceeding

• Thread enters the blocked state while it waits for a requested, unavailable resource (e.g. I/O device)

• When a running thread calls function time.sleep, it releases the GIL and enters the sleeping state

2002 Prentice Hall. All rights reserved.

19.2 Thread States: Life Cycle of a Thread

• Deadlock occurs when one or more threads wait forever for an event that cannot occur

• In indefinite postponement, one or more threads is delayed for some unpredictably long time

2002 Prentice Hall. All rights reserved.

19.2 Thread States: Life Cycle of a Thread

ready

running

waiting sleeping dead blocked

born

start

obtainGIL

GILquantum

expira tion

request

unavailable resource

sleepwai

t

sleep interval expires

obta

in resourc e

notify

comple te

notifyAll

Fig. 19.1 State diagram showing the life cycle of a thread.

2002 Prentice Hall. All rights reserved.

19.2 Thread States: Life Cycle of a Thread

• Module threading provides ways for a program to obtain information about its threads, including their current states– Function threading.currentThread returns

reference to currently running Thread object

– Function threading.enumerate returns list of currently active Thread objects

– Function threading.activeCount returns length of list returned by threading.enumerate

– Method isAlive returns 1 if the Thread object’s start method bas been invoked and the Thread object is not dead

– Methods setName and getName allow programmer to set and get a Thread object’s name, respectively

2002 Prentice Hall. All rights reserved.

19.3 threading.Thread example

• Threads can be created by defining a class that derives from threading.Thread and instantiating objects of that class

2002 Prentice Hall.All rights reserved.

Outline

fig19_02.py

1 # Fig. 19.2: fig19_02.py2 # Multiple threads printing at different intervals.3 4 import threading5 import random6 import time7 8 class PrintThread( threading.Thread ):9 """Subclass of threading.Thread"""10 11 def __init__( self, threadName ):12 """Initialize thread, set sleep time, print data"""13 14 threading.Thread.__init__( self, name = threadName )15 self.sleepTime = random.randrange( 1, 6 )16 print "Name: %s; sleep: %d" % \17 ( self.getName(), self.sleepTime )18 19 # overridden Thread run method20 def run( self ):21 """Sleep for 1-5 seconds"""22 23 print "%s going to sleep for %s second(s)" % \24 ( self.getName(), self.sleepTime )25 time.sleep( self.sleepTime )26 print self.getName(), "done sleeping"27 28 thread1 = PrintThread( "thread1" )29 thread2 = PrintThread( "thread2" )30 thread3 = PrintThread( "thread3" )31 32 print "\nStarting threads"33

Module threading provides multithreading capabilities

Thread class derived from base class threading.Thread

Calls base-class constructor with object and the thread’s name

Keyword name specifies Thread object’s nameThread object has randomly selected sleepTime attribute

Return Thread object’s name

Overridden Thread run method

Instantiate PrintThread object

2002 Prentice Hall.All rights reserved.

Outline

fig19_02.py

34 thread1.start() # invokes run method of thread135 thread2.start() # invokes run method of thread236 thread3.start() # invokes run method of thread337 38 print "Threads started\n"

Name: thread1; sleep: 5Name: thread2; sleep: 1Name: thread3; sleep: 3

Starting threadsName: thread1; sleep: 5Name: thread2; sleep: 1Name: thread3; sleep: 3

Starting threadsthread1 going to sleep for 5 second(s)thread2 going to sleep for 1 second(s)thread3 going to sleep for 3 second(s)Threads started

thread2 done sleepingthread3 done sleepingthread1 done sleeping

Thread object enters ready state by invoking start method

2002 Prentice Hall. All rights reserved.

19.4 Thread Synchronization

• Sections of code that access shared data are referred to as critical sections

• Mutual exclusion or thread synchronization ensures a thread accessing the shared data excludes all other threads from doing so simultaneously

• Module threading provides several thread-synchronization mechanisms (e.g., locks and condition variables)

2002 Prentice Hall. All rights reserved.

19.4 Thread Synchronization

• Class threading.RLock creates lock objects– Method acquire causes lock to enter locked state

– Only one thread may acquire lock at a time so system places any other threads who attempt to acquire lock in blocked state

– When the thread that owns the lock invokes method release, the lock enters the unlocked state and blocked threads receive a notification so one can acquire the lock

2002 Prentice Hall. All rights reserved.

19.4 Thread Synchronization

• Condition variables monitor the state of an object or notify a thread when an event occurs– Created with class threading.Condition– Provides acquire and release method because

condition variable contains underlying locks

– Method wait causes calling thread to release lock lock and block until it is awakened again

– Method notify wakes up one thread waiting on the condition variable

– Method notifyAll wakes up all waiting threads

2002 Prentice Hall. All rights reserved.

19.5 Producer/Consumer Relationship without Thread Synchronization

• Producer portion of application generates data• Consumer portion of application uses that data• Producer thread calls produce method to generate

data and place it into a shared memory region called a buffer

• Consumer thread calls consumer method to read data from the buffer

2002 Prentice Hall.All rights reserved.

Outline

fig19_03.py

1 # Fig. 19.3: fig19_03.py2 # Multiple threads modifying shared object.3 4 from UnsynchronizedInteger import UnsynchronizedInteger5 from ProduceInteger import ProduceInteger6 from ConsumeInteger import ConsumeInteger7 8 # initialize integer and threads9 number = UnsynchronizedInteger()10 producer = ProduceInteger( "Producer", number, 1, 4 )11 consumer = ConsumeInteger( "Consumer", number, 4 )12 13 print "Starting threads...\n"14 15 # start threads16 producer.start()17 consumer.start()18 19 # wait for threads to terminate20 producer.join()21 consumer.join()22 23 print "\nAll threads have terminated."

Create shared UnsynchronizedInteger object numberInstantiate ProduceInteger thread object

Create ConsumeInteger thread object

Invoke thread object’s start methods

Ensures that main program waits for both threads to terminate before proceeding

2002 Prentice Hall.All rights reserved.

Outline

fig19_03.py

Starting threads...

Consumer reads –1Consumer reads –1Producer writes 1Consumer reads 1Producer writes 2Consumer reads 2Consumer read values totaling: 1.Terminating Consumer.Producer writes 3Producer writes 4Producer done producing.Terminating Producer.

All threads have terminated.

Starting threads...

Producer writes 1Producer writes 2Producer writes 3Consumer reads 3Producer writes 4Producer done producing.Terminating Producer.Consumer reads 4Consumer reads 4Consumer reads 4Consumer read values totaling: 15.Terminating Consumer.

All threads have terminated.

2002 Prentice Hall.All rights reserved.

Outline

Starting threads...

Producer writes 1Consumer reads 1Producer writes 2Consumer reads 2Producer writes 3Consumer reads 3Producer writes 4Producer done producing.Terminating Producer.Consumer reads 4Consumer read values totaling: 10.Terminating Consumer.

All threads have terminated

2002 Prentice Hall.All rights reserved.

Outline

ProduceInteger.py

1 # Fig. 19.4: ProduceInteger.py2 # Integer-producing class.3 4 import threading5 import random6 import time7 8 class ProduceInteger( threading.Thread ):9 """Thread to produce integers"""10 11 def __init__( self, threadName, sharedObject, begin, end ):12 """Initialize thread, set shared object"""13 14 threading.Thread.__init__( self, name = threadName )15 self.sharedObject = sharedObject16 self.begin = begin17 self.end = end18 19 def run( self ):20 """Produce integers in given range at random intervals"""21 22 for i in range( self.begin, ( self.end + 1 ) ):23 time.sleep( random.randrange( 4 ) )24 self.sharedObject.set( i )25 26 print "%s done producing." % self.getName()27 print "Terminating %s." % self.getName()

Subclass of threading.Thread

Set variable sharedObject to refer to object shared by producer and consumer

Loops from begin to endPut ProduceInteger object into sleeping state for random time interval

Prints message indicating that run method has terminated

Set shared object

2002 Prentice Hall.All rights reserved.

Outline

ConsumeInteger.py

1 # Fig. 19.5: ConsumeInteger.py2 # Integer-consuming queue.3 4 import threading5 import random6 import time7 8 class ConsumeInteger( threading.Thread ):9 """Thread to consume integers"""10 11 def __init__( self, threadName, sharedObject, amount ):12 """Initialize thread, set shared object"""13 14 threading.Thread.__init__( self, name = threadName )15 self.sharedObject = sharedObject16 self.amount = amount17 18 def run( self ):19 """Consume given amount of values at random time intervals"""20 21 sum = 0 # total sum of consumed values22 23 # consume given amount of values24 for i in range( self.amount ):25 time.sleep( random.randrange( 4 ) )26 sum += self.sharedObject.get()27 28 print "%s read values totaling: %d." % \29 ( self.getName(), sum )30 print "Terminating %s." % self.getName()

Refers to the shared UnsynchronizedInteger object

Sums values of the shared UnsynchronizedInteger object

Print total sum of consumed values

2002 Prentice Hall.All rights reserved.

Outline

UnsynchronizedInteger.py

1 # Fig. 19.6: UnsynchronizedInteger.py2 # Unsynchronized access to an integer.3 4 import threading5 6 class UnsynchronizedInteger:7 """Class that provides unsynchronized access an integer"""8 9 def __init__( self ):10 """Initialize integer to -1"""11 12 self.buffer = -113 14 def set( self, newNumber ):15 """Set value of integer"""16 17 print "%s writes %d" % \18 ( threading.currentThread().getName(), newNumber )19 self.buffer = newNumber20 21 def get( self ):22 """Get value of integer"""23 24 tempNumber = self.buffer25 print "%s reads %d" % \26 ( threading.currentThread().getName(), tempNumber )27 28 return tempNumber

Provides unsynchronized access to an integer

Initialize integer to -1Sets integer to specified value

Returns value of integer

Use temporary variable to ensure that program prints correct value

2002 Prentice Hall. All rights reserved.

19.6 Producer/Consumer Relationship with Thread Synchronization

• Producer and consumer can access shared memory cell with synchronization– Ensures that the consumer consumes each value exactly once

– Ensures that consumer waits for producer to execute first

2002 Prentice Hall.All rights reserved.

Outline

fig19_07.py

1 # Fig. 19.7: fig19_07.py2 # Multiple threads modifying shared object.3 4 from SynchronizedInteger import SynchronizedInteger5 from ProduceInteger import ProduceInteger6 from ConsumeInteger import ConsumeInteger7 8 # initialize number and threads9 number = SynchronizedInteger()10 producer = ProduceInteger( "Producer", number, 1, 4 )11 consumer = ConsumeInteger( "Consumer", number, 4 )12 13 print "Starting threads...\n"14 15 print "%-35s %-9s%2s\n" % \16 ( "Operation" , "Buffer", "Occupied Count" )17 number.displayState( "Initial state" )18 19 # start threads20 producer.start()21 consumer.start()22 23 # wait for threads to terminate24 producer.join()25 consumer.join()26 27 print "\nAll threads have terminated."

Create SynchronizedInteger object

Start producer and consumer threads

2002 Prentice Hall.All rights reserved.

Outline

 Starting threads...

Operation Buffer Occupied Count

Initial state -1 0

Producer writes 1 1

Consumer reads 1 1 0

Consumer tries to read.Buffer empty. Consumer waits. 1 0

Producer writes 2 2 1

Consumer reads 2 2 0

Producer writes 3 3 1

Producer tries to write.Buffer full. Producer waits. 3 1

Consumer reads 3 3 0

Producer writes 4 4 1

Producer done producing.Terminating Producer.Consumer reads 4 4 0

Consumer read values totaling: 10.Terminating Consumer.

All threads have terminated.

2002 Prentice Hall.All rights reserved.

Outline

Starting threads...

Operation Buffer Occupied Count

Initial state -1 0

Consumer tries to read.Buffer empty. Consumer waits. -1 0

Producer writes 1 1 1

Consumer reads 1 1 0

Producer writes 2 2 1

Consumer reads 2 2 0

Consumer tries to read.Buffer empty. Consumer waits. 2 0

Producer writes 3 3 1

Consumer reads 3 3 0

Producer writes 4 4 1

Producer done producing.Terminating Producer.Consumer reads 4 4 0

Consumer read values totaling: 10.Terminating Consumer.

All threads have terminated.

2002 Prentice Hall.All rights reserved.

Outline

Starting threads...

Operation Buffer Occupied Count

Initial state -1 0

Producer writes 1 1 1

Consumer reads 1 1 0

Producer writes 2 2 1

Consumer reads 2 2 0

Producer writes 3 3 1

Consumer reads 3 3 0

 

Producer writes 4 4 1

Producer done producing.Terminating Producer.Consumer reads 4 4 0

Consumer read values totaling: 10.Terminating Consumer.

All threads have terminated.

2002 Prentice Hall.All rights reserved.

Outline

SynchronizedInteger.py

1 # Fig. 19.8: SynchronizedInteger.py2 # Synchronized access to an integer with condition variable.3 4 import threading5 6 class SynchronizedInteger:7 """Class that provides synchronized access an integer"""8 9 def __init__( self ):10 """Initialize integer, buffer count and condition variable"""11 12 self.buffer = -1 13 self.occupiedBufferCount = 0 # number of occupied buffers14 self.threadCondition = threading.Condition()15 16 def set( self, newNumber ):17 """Set value of integer--blocks until lock acquired"""18 19 # block until lock released then acquire lock20 self.threadCondition.acquire() 21 22 # while not producer’s turn, release lock and block23 while self.occupiedBufferCount == 1:24 print "%s tries to write." % \25 threading.currentThread().getName()26 self.displayState( "Buffer full. " + \27 threading.currentThread().getName() + " waits." )28 self.threadCondition.wait()29 30 # (lock has now been re-acquired)31 32 self.buffer = newNumber # set new buffer value33 self.occupiedBufferCount += 1 # allow consumer to consume34

Provides synchronized access to an integer

Keeps track of number of occupied buffersCondition variable controls access to integer

Sets value of integer, blocking until lock acquired

Method acquire blocks until lock available

Block until producer’s turn

Set new buffer value after lock is re-acquired

2002 Prentice Hall.All rights reserved.

Outline

SynchronizedInteger.py

35 self.displayState( "%s writes %d" % \36 ( threading.currentThread().getName(), newNumber ) )37 38 self.threadCondition.notify() # wake up a waiting thread39 self.threadCondition.release() # allow lock to be acquired40 41 def get( self ):42 """Get value of integer--blocks until lock acquired"""43 44 # block until lock released then acquire lock45 self.threadCondition.acquire() 46 47 # while producer’s turn, release lock and block48 while self.occupiedBufferCount == 0:49 print "%s tries to read." % \50 threading.currentThread().getName()51 self.displayState( "Buffer empty. " + \52 threading.currentThread().getName() + " waits." )53 self.threadCondition.wait() 54 55 # (lock has now been re-acquired)56 57 tempNumber = self.buffer58 self.occupiedBufferCount -= 1 # allow producer to produce59 60 self.displayState( "%s reads %d" % \61 ( threading.currentThread().getName(), tempNumber ) )62 63 self.threadCondition.notify() # wake up a waiting thread64 self.threadCondition.release() # allow lock to be acquired65 66 return tempNumber67

Wake up a waiting threadAllow lock to be acquired

Return value of integer when lock acquired

Release lock and block until consumer’s turn

Display buffer state

2002 Prentice Hall.All rights reserved.

Outline

SynchronizedInteger.py

68 def displayState( self, operation ):69 """Display current state"""70 71 print "%-35s %-9s%2s\n" % \72 ( operation, self.buffer, self.occupiedBufferCount )

Displays current state of buffer

2002 Prentice Hall. All rights reserved.

19.7 Producer/Consumer Relationship: Module Queue

• Module Queue defines class Queue, which is a synchronized implementation of a queue

• Queue constructor accepts optional argument maxsize

• Consumer consumes value by calling Queue method get, which removes and returns item from the head of the queue

• Producer produces value by calling Queue method put, which inserts item at tail of queue

2002 Prentice Hall.All rights reserved.

Outline

fig19_09.py

1 # Fig. 19.9: fig19_09.py2 # Multiple threads producing/consuming values.3 4 from Queue import Queue5 from ProduceToQueue import ProduceToQueue6 from ConsumeFromQueue import ConsumeFromQueue7 8 # initialize number and threads9 queue = Queue()10 producer = ProduceToQueue( "Producer", queue )11 consumer = ConsumeFromQueue( "Consumer", queue )12 13 print "Starting threads...\n"14 15 # start threads16 producer.start()17 consumer.start()18 19 # wait for threads to terminate20 producer.join()21 consumer.join()22 23 print "\nAll threads have terminated."

Starting threads...

Producer adding 11 to queueProducer adding 12 to queueConsumer attempting to read 11...Consumer read 11

Class Queue is synchronized queue implementation

Instantiate infinite-size queue

Start producer and consumer threads

Main program waits for consumer and producer threads to terminate

2002 Prentice Hall.All rights reserved.

Outline

fig19_09.py

Consumer attempting to read 12...Consumer read 12Producer adding 13 to queueConsumer attempting to read 13...Consumer read 13Producer adding 14 to queueConsumer attempting to read 14...Consumer read 14Consumer attempting to read 15...Producer adding 15 to queueConsumer read 15Consumer attempting to read 16...Producer adding 16 to queueConsumer read 16Consumer attempting to read 17...Producer adding 17 to queueConsumer read 17Producer adding 18 to queueProducer adding 19 to queueConsumer attempting to read 18...Consumer read 18Consumer attempting to read 19...Consumer read 19Producer adding 20 to queueProducer finished producing valuesTerminating ProducerConsumer attempting to read 20...Consumer read 20Consumer retrieved values totaling: 155Terminating ConsumerAll threads have terminated.

2002 Prentice Hall.All rights reserved.

Outline

ProduceToQueue.py

1 # Fig. 19.10: ProduceToQueue.py2 # Integer-producing class.3 4 import threading5 import random6 import time7 8 class ProduceToQueue( threading.Thread ):9 """Thread to produce integers"""10 11 def __init__( self, threadName, queue ):12 """Initialize thread, set shared queue"""13 14 threading.Thread.__init__( self, name = threadName )15 self.sharedObject = queue16 17 def run( self ):18 """Produce integers in range 11-20 at random intervals"""19 20 for i in range( 11, 21 ):21 time.sleep( random.randrange( 4 ) )22 print "%s adding %s to queue" % ( self.getName(), i )23 self.sharedObject.put( i )24 25 print self.getName(), "finished producing values"26 print "Terminating", self.getName()

Producer thread uses synchronized queue

Refers to shared synchronized Queue object

Place item at tail of the queue

2002 Prentice Hall.All rights reserved.

Outline

ConsumeFromQueue.py

1 # Fig. 19.11: ConsumeFromQueue.py2 # Integer-consuming queue.3 4 import threading5 import random6 import time7 8 class ConsumeFromQueue( threading.Thread ):9 """Thread to consume integers"""10 11 def __init__( self, threadName, queue ):12 """Initialize thread, set shared queue"""13 14 threading.Thread.__init__( self, name = threadName )15 self.sharedObject = queue16 17 def run( self ):18 """Consume 10 values at random time intervals"""19 20 sum = 0 # total sum of consumed values21 current = 10 # last value retrieved22 23 # consume 10 values24 for i in range( 10 ):25 time.sleep( random.randrange( 4 ) )26 print "%s attempting to read %s..." % \27 ( self.getName(), current + 1 )28 current = self.sharedObject.get()29 print "%s read %s" % ( self.getName(), current )30 sum += current31 32 print "%s retrieved values totaling: %d" % \33 ( self.getName(), sum )34 print "Terminating", self.getName()

Consumer thread using synchronized Queue object

Refers to shared Queue object

Get item from head of queue

2002 Prentice Hall. All rights reserved.

19.8 Producer/Consumer Relationship: The Circular Buffer

• Circular buffer provides extra buffers into which the producer can place values and from which the consumer can retrieve those values

2002 Prentice Hall.All rights reserved.

Outline

fig19_12.py

1 # Fig. 19.12: fig19_12.py2 # Show multiple threads modifying shared object.3 4 from CircularBuffer import CircularBuffer5 from ProduceInteger import ProduceInteger6 from ConsumeInteger import ConsumeInteger7 8 # initialize number and threads9 buffer = CircularBuffer()10 producer = ProduceInteger( "Producer", buffer, 11, 20 )11 consumer = ConsumeInteger( "Consumer", buffer, 10 )12 13 print "Starting threads...\n"14 15 buffer.displayState()16 17 # start threads18 producer.start()19 consumer.start()20 21 # wait for threads to terminate22 producer.join()23 consumer.join()24 25 print "\nAll threads have terminated."

Create CircularBuffer object

Start producer and consumer threads

2002 Prentice Hall.All rights reserved.

Outline Starting threads... (buffers occupied: 0)buffers: -1 -1 -1 ---- ---- ---- WR  Producer writes 11 (buffers occupied: 1)buffers: 11 -1 -1 ---- ---- ---- R W  Consumer reads 11 (buffers occupied: 0)buffers: 11 -1 -1 ---- ---- ---- WR   All buffers empty. Consumer waits.Producer writes 12 (buffers occupied: 1)buffers: 11 12 -1 ---- ---- ---- R W  Consumer reads 12 (buffers occupied: 0)buffers: 11 12 -1 ---- ---- ---- WR  All buffers empty. Consumer waits.Producer writes 13 (buffers occupied: 1)buffers: 11 12 13 ---- ---- ---- W R  

2002 Prentice Hall.All rights reserved.

OutlineConsumer reads 13 (buffers occupied: 0)buffers: 11 12 13 ---- ---- ---- WR  All buffers empty. Consumer waits.Producer writes 14 (buffers occupied: 1)buffers: 14 12 13 ---- ---- ---- R W  Consumer reads 14 (buffers occupied: 0)buffers: 14 12 13 ---- ---- ---- WR  Producer writes 15 (buffers occupied: 1)buffers: 14 15 13 ---- ---- ---- R W  Producer writes 16 (buffers occupied: 2)buffers: 14 15 16 ---- ---- ---- W R  Consumer reads 15 (buffers occupied: 1)buffers: 14 15 16 ---- ---- ---- W R  Producer writes 17 (buffers occupied: 2)buffers: 17 15 16 ---- ---- ---- W R

2002 Prentice Hall.All rights reserved.

OutlineProducer writes 18 (buffers occupied: 3)buffers: 17 18 16 ---- ---- ---- WR  All buffers full. Producer waits.Consumer reads 16 (buffers occupied: 2)buffers: 17 18 16 ---- ---- ---- R W  Producer writes 19 (buffers occupied: 3)buffers: 17 18 19 ---- ---- ---- WR  All buffers full. Producer waits.Consumer reads 17 (buffers occupied: 2)buffers: 17 18 19 ---- ---- ---- W R  Producer writes 20 (buffers occupied: 3)buffers: 20 18 19 ---- ---- ---- WR

Producer done producing.Terminating Producer. Consumer reads 18 (buffers occupied: 2)buffers: 20 18 19 ---- ---- ---- W R

2002 Prentice Hall.All rights reserved.

OutlineConsumer reads 19 (buffers occupied: 1)buffers: 20 18 19 ---- ---- ---- R W  Consumer reads 20 (buffers occupied: 0)buffers: 20 18 19 ---- ---- ---- WR  Consumer read values totaling: 155.Terminating Consume\r. All threads have terminated

2002 Prentice Hall.All rights reserved.

Outline

CircularBuffer.py

1 # Fig. 19.13: CircularBuffer.py2 # Synchronized circular buffer of integer values3 4 import threading5 6 class CircularBuffer:7 8 def __init__( self ):9 """Set buffer, count, locations and condition variable"""10 11 # each element in list is a buffer12 self.buffer = [ -1, -1, -1 ]13 14 self.occupiedBufferCount = 0 # count of occupied buffers15 self.readLocation = 0 # current reading index16 self.writeLocation = 0 # current writing index17 18 self.threadCondition = threading.Condition()19 20 def set( self, newNumber ):21 """Set next buffer index value--blocks until lock acquired"""22 23 # block until lock released then acquire lock 24 self.threadCondition.acquire()25 26 # while all buffers are full, release lock and block 27 while self.occupiedBufferCount == len( self.buffer ):28 print "All buffers full. %s waits." % \29 threading.currentThread().getName()30 self.threadCondition.wait()31 32 # (there is an empty buffer, lock has been re-acquired)33

Three-element integer list that represents the circular buffer

Number of occupied buffersCurrent reading indexCurrent writing index

Condition variable protects access to circular buffer

Release lock and block while all buffers are full

2002 Prentice Hall.All rights reserved.

Outline

CircularBuffer.py

34 # place value in writeLocation of buffer35 # print string indicating produced value36 self.buffer[ self.writeLocation ] = newNumber37 print "%s writes %d " % \38 ( threading.currentThread().getName(), newNumber ),39 40 # produced value, so increment number of occupied buffers41 self.occupiedBufferCount += 142 43 # update writeLocation for future write operation44 # add current state to output45 self.writeLocation = ( self.writeLocation + 1 ) % \46 len( self.buffer )47 self.displayState() 48 49 self.threadCondition.notify() # wake up a waiting thread50 self.threadCondition.release() # allow lock to be acquired51 52 def get( self ):53 """Get next buffer index value--blocks until lock acquired"""54 55 # block until lock released then acquire lock56 self.threadCondition.acquire()57 58 # while all buffers are empty, release lock and block59 while self.occupiedBufferCount == 0:60 print "All buffers empty. %s waits." % \61 threading.currentThread().getName()62 self.threadCondition.wait()63 64 # (there is a full buffer, lock has been re-acquired)65

Place value at write index

Update write index for future write operations

Release lock and block while buffers are empty

2002 Prentice Hall.All rights reserved.

Outline

CircularBuffer.py

66 # obtain value at current readLocation67 # print string indicating consumed value68 tempNumber = self.buffer[ self.readLocation ]69 print "%s reads %d " % ( threading.currentThread().getName(),70 tempNumber ),71 72 # consumed value, so decrement number of occupied buffers73 self.occupiedBufferCount -= 174 75 # update readLocation for future read operation76 # add current state to output77 self.readLocation = ( self.readLocation + 1 ) % \78 len( self.buffer )79 self.displayState() 80 81 self.threadCondition.notify() # wake up a waiting thread82 self.threadCondition.release() # allow lock to be acquired83 84 return tempNumber85 86 def displayState( self ):87 """Display current state"""88 89 # display first line of state information90 print "(buffers occupied: %d)" % self.occupiedBufferCount91 print "buffers: ",92 93 for item in self.buffer:94 print " %d " % item,95 96 # display second line of state information97 print "\n ",98 99 for item in self.buffer:100 print "---- ",

Obtain and print value at current read index

Update read index for future read operations

Display current circular buffer state

2002 Prentice Hall.All rights reserved.

Outline

CircularBuffer.py

101 102 # display third line of state information103 print "\n ",104 105 for i in range( len( self.buffer ) ):106 107 if ( i == self.writeLocation ) and \108 ( self.writeLocation == self.readLocation ):109 print " WR ",110 elif ( i == self.writeLocation ):111 print " W ",112 elif ( i == self.readLocation ):113 print " R ",114 else:115 print " ",116 117 print "\n"

2002 Prentice Hall. All rights reserved.

19.9 Semaphores

• Semaphore: variable that controls access to common resource or critical section

• Maintains a counter that specifies number of threads that can use the resource or enter the critical section simultaneously

• Counter decremented each time a thread acquires the semaphore

• When counter reaches zero, semaphore blocks other threads from accessing semaphore until it has been released by another thread

2002 Prentice Hall.All rights reserved.

Outline

fig19_14.py

1 # Figure 19.14: fig19_14.py2 # Semaphore to control access to a critical section.3 4 import threading5 import random6 import time7 8 class SemaphoreThread( threading.Thread ):9 """Class using semaphores"""10 11 availableTables = [ "A", "B", "C", "D", "E" ]12 13 def __init__( self, threadName, semaphore ):14 """Initialize thread"""15 16 threading.Thread.__init__( self, name = threadName )17 self.sleepTime = random.randrange( 1, 6 )18 19 # set the semaphore as a data attribute of the class20 self.threadSemaphore = semaphore 21 22 def run( self ):23 """Print message and release semaphore"""24 25 # acquire the semaphore26 self.threadSemaphore.acquire()27 28 # remove a table from the list29 table = SemaphoreThread.availableTables.pop()30 print "%s entered; seated at table %s." % \31 ( self.getName(), table ), 32 print SemaphoreThread.availableTables33 34 time.sleep( self.sleepTime ) # enjoy a meal35

Class using semaphores

Set semaphore as a data attribute

Acquire the semaphore

Remove a table from the list

Thread sleeps (i.e., customer enjoys a meal)

2002 Prentice Hall.All rights reserved.

Outline

fig19_14.py

36 # free a table37 print " %s exiting; freeing table %s." % \38 ( self.getName(), table ),39 SemaphoreThread.availableTables.append( table )40 print SemaphoreThread.availableTables41 42 # release the semaphore after execution finishes43 self.threadSemaphore.release()44 45 threads = [] # list of threads46 47 # semaphore allows five threads to enter critical section48 threadSemaphore = threading.Semaphore(49 len( SemaphoreThread.availableTables ) )50 51 # create ten threads52 for i in range( 1, 11 ):53 threads.append( SemaphoreThread( "thread" + str( i ),54 threadSemaphore ) )55 56 # start each thread57 for thread in threads:58 thread.start()

Free table by appending it to list

Release semaphore after critical section executes

Create threading.Semaphore and set internal counter to five

Start ten threads

2002 Prentice Hall.All rights reserved.

Outline

fig19_14.py

thread1 entered; seated at table E. ['A', 'B', 'C', 'D']thread2 entered; seated at table D. ['A', 'B', 'C']thread3 entered; seated at table C. ['A', 'B']thread4 entered; seated at table B. ['A']thread5 entered; seated at table A. [] thread2 exiting; freeing table D. ['D']thread6 entered; seated at table D. [] thread1 exiting; freeing table E. ['E']thread7 entered; seated at table E. [] thread3 exiting; freeing table C. ['C']thread8 entered; seated at table C. [] thread4 exiting; freeing table B. ['B']thread9 entered; seated at table B. [] thread5 exiting; freeing table A. ['A']thread10 entered; seated at table A. [] thread7 exiting; freeing table E. ['E'] thread8 exiting; freeing table C. ['E', 'C'] thread9 exiting; freeing table B. ['E', 'C', 'B'] thread10 exiting; freeing table A. ['E', 'C', 'B', 'A'] thread6 exiting; freeing table D. ['E', 'C', 'B', 'A', 'D']

2002 Prentice Hall. All rights reserved.

19.10 Events

• Module threading defines class Event, which is useful for thread communication

• Event objects have an internal, true or false flag• One or more threads call the Event object’s wait method to block until the event occurs

• When the event occurs, the blocked thread or threads are awakened in the order that they arrived and resume execution

2002 Prentice Hall.All rights reserved.

Outline

fig19_15.py

1 # Fig. 19.15: fig19_15.py2 # Event objects.3 4 import threading5 import random6 import time7 8 class VehicleThread( threading.Thread ):9 """Class representing a motor vehicle at an intersection"""10 11 def __init__( self, threadName, event ):12 """Initializes thread"""13 14 threading.Thread.__init__( self, name = threadName )15 16 # ensures that each vehicle waits for a green light17 self.threadEvent = event18 19 def run( self ):20 """Vehicle waits unless/until light is green"""21 22 # stagger arrival times23 time.sleep( random.randrange( 1, 10 ) )24 25 # prints arrival time of car at intersection26 print "%s arrived at %s" % \27 ( self.getName(), time.ctime( time.time() ) )28 29 # flag is false until light is green30 self.threadEvent.wait()31 32 # displays time that car departs intersection33 print "%s passes through intersection at %s" % \34 ( self.getName(), time.ctime( time.time() ) )35

VehicleThread objects represent motor vehicle at an intersection

Each thread has Event object as data attribute

Thread is in blocked state until light is green

Displays message after Event flag becomes true

2002 Prentice Hall.All rights reserved.

Outline

fig19_15.py

36 greenLight = threading.Event()37 vehicleThreads = []38 39 # creates and starts ten Vehicle threads40 for i in range( 1, 11 ):41 vehicleThreads.append( VehicleThread( "Vehicle" + str( i ),42 greenLight ) )43 44 for vehicle in vehicleThreads:45 vehicle.start()46 47 while threading.activeCount() > 1:48 49 # sets the Event's flag to false -- block all incoming vehicles50 greenLight.clear()51 print "RED LIGHT! at", time.ctime( time.time() )52 time.sleep( 3 )53 54 # sets the Event's flag to true -- awaken all waiting vehicles55 print "GREEN LIGHT! at", time.ctime( time.time() )56 greenLight.set()57 time.sleep( 1 )

Instantiate Event object

Start eleven VehicleThread objects

Set Event object’s flag to false

Set Event object’s flag to true

2002 Prentice Hall.All rights reserved.

Outline

fig19_15.py

RED LIGHT! at Fri Dec 21 18:10:44 2001Vehicle7 arrived at Fri Dec 21 18:10:45 2001Vehicle2 arrived at Fri Dec 21 18:10:46 2001Vehicle8 arrived at Fri Dec 21 18:10:46 2001GREEN LIGHT! at Fri Dec 21 18:10:47 2001Vehicle7 passes through intersection at Fri Dec 21 18:10:47 2001Vehicle2 passes through intersection at Fri Dec 21 18:10:47 2001Vehicle8 passes through intersection at Fri Dec 21 18:10:47 2001Vehicle1 arrived at Fri Dec 21 18:10:48 2001Vehicle1 passes through intersection at Fri Dec 21 18:10:48 2001Vehicle9 arrived at Fri Dec 21 18:10:48 2001Vehicle9 passes through intersection at Fri Dec 21 18:10:48 2001Vehicle10 arrived at Fri Dec 21 18:10:48 2001Vehicle10 passes through intersection at Fri Dec 21 18:10:48 2001RED LIGHT! at Fri Dec 21 18:10:48 2001Vehicle4 arrived at Fri Dec 21 18:10:50 2001Vehicle5 arrived at Fri Dec 21 18:10:50 2001Vehicle6 arrived at Fri Dec 21 18:10:51 2001GREEN LIGHT! at Fri Dec 21 18:10:51 2001Vehicle4 passes through intersection at Fri Dec 21 18:10:51 2001Vehicle5 passes through intersection at Fri Dec 21 18:10:51 2001Vehicle6 passes through intersection at Fri Dec 21 18:10:51 2001RED LIGHT! at Fri Dec 21 18:10:52 2001Vehicle3 arrived at Fri Dec 21 18:10:53 2001GREEN LIGHT! at Fri Dec 21 18:10:55 2001Vehicle3 passes through intersection at Fri Dec 21 18:10:55 2001