views:

86

answers:

2

I've got an exam coming up soon. I've been looking through the past paper, this question keeps bugging me. I can't seem to find what the bug in the program could be because I'm quite new to all this. Can anyone help me out?

The following program contains a bug. Determine what kind of problem the program exhibits, and show how it can be fixed.

import threading
import time
import random
#the list "data" must contain two values.  
#The second must always be equal to the first multiplied by 4

class GeneratorThread(threading.Thread):
    #Thread for generating value pairs
    def __init__(self,data):
        threading.Thread.__init__(self)
        self.data=data
    def run(self):
        while True:
            #Pick a new first number
            num=random.randint(0,100)
            self.data[0]=num
            #simulate some processing 
          #to calculate second number
            time.sleep(1)
            #Place second value into ata
            self.data[1]=num*4
            time.sleep(1)
class ProcessorThread(threading.Thread):
    #Thread for processing value pairs
    def __init__(self,data):
        threading.Thread.__init__(self)
        self.data=data
    def run(self):
        while True:
            #Process current data
            num1=self.data[0]
            num2=self.data[1]
            print "Values are %d and %d."%(num1,num2)
            if num2!=num1*4:
                print "\tDATA INCONSISTENCY!"
            time.sleep(2)
if __name__=="__main__":
    data=[1,4]
    t1=GeneratorThread(data)
    t2=ProcessorThread(data)
    t1.start()
    t2.start()
+4  A: 

There's a race condition between your two threads. Basically, there is a time between setting data[0] to something before setting data[1] to four times that value.

If the second thread comes in and checks the values during that time, the data inconsistency will occur.

You may think the two threads refer to their own data information but that's not the case. They are both references to the main data array. If you really want them to have their own data arrays, you should change:

self.data=data

into:

self.data=data[:]

in both init functions.

Otherwise (and this is the more likely case where you want to share the data), you need to ensure your threads are properly synchronised so that the data is always consistent, something like using a mutex:

#!/usr/bin/python

import threading
import time
import random
#the list "data" must contain two values.
#The second must always be equal to the first multiplied by 4

class GeneratorThread(threading.Thread):
    #Thread for generating value pairs
    def __init__(self,data):
        threading.Thread.__init__(self)
        self.datamutex=datamutex         # save reference to the mutex.
        self.data=data
    def run(self):
        while True:
            #Pick a new first number
            num=random.randint(0,100)
            self.datamutex.acquire()     # get the mutex.
            self.data[0]=num
            #simulate some processing
          #to calculate second number
            time.sleep(1)
            #Place second value into ata
            self.data[1]=num*4
            self.datamutex.release()     # release it to allow other thread
                                         #  to run now that data is consistent.
            time.sleep(1)

 

class ProcessorThread(threading.Thread):
    #Thread for processing value pairs
    def __init__(self,data):
        threading.Thread.__init__(self)
        self.datamutex=datamutex         # save mutex reference.
        self.data=data
    def run(self):
        while True:
            #Process current data
            self.datamutex.acquire()     # lock (can only happen if data consistent).
            num1=self.data[0]
            num2=self.data[1]
            self.datamutex.release()     # release it to allow updates.

            print "Values are %d and %d."%(num1,num2)
            if num2!=num1*4:
                print "\tDATA INCONSISTENCY!"
            time.sleep(2)

if __name__=="__main__":
    datamutex = threading.Lock()         # Create the mutex for both threads.
    data=[1,4]
    t1=GeneratorThread(data)
    t2=ProcessorThread(data)
    t1.start()
    t2.start()

Now locks aren't the only way to synchronise threads but they're fine for this particular case.

paxdiablo
A: 

There is no need for explicit locking if you stick to thread safe ways of updating and reading the data. In this case using the fact that slice operations on lists are thread safe.

import threading
import time
import random
#the list "data" must contain two values.  
#The second must always be equal to the first multiplied by 4

class GeneratorThread(threading.Thread):
    #Thread for generating value pairs
    def __init__(self,data):
        threading.Thread.__init__(self)
        self.data=data
    def run(self):
        while True:
            #Pick a new first number
            num=random.randint(0,100)
            data0=num
            #simulate some processing 
          #to calculate second number
            time.sleep(1)
            #Place second value into ata
            data1=num*4
            self.data[0:2]=data0,data1
            time.sleep(1)
class ProcessorThread(threading.Thread):
    #Thread for processing value pairs
    def __init__(self,data):
        threading.Thread.__init__(self)
        self.data=data
    def run(self):
        while True:
            #Process current data
            num1,num2=self.data[0:2]
            print "Values are %d and %d."%(num1,num2)
            if num2!=num1*4:
                print "\tDATA INCONSISTENCY!"
            time.sleep(2) 
if __name__=="__main__":
    data=[1,4]
    t1=GeneratorThread(data)
    t2=ProcessorThread(data)
    t1.start()
    t2.start()
gnibbler