1

The provided code is about 2 thread trying to access the function increment() to increment the value of a global variable x. I have designed a semaphore class for process synchronization. So the expected increment of each thread is expected to be 1000000 summing up to 2000000. But actual output is not reaching up to 2000000. The output is reaching up to 1800000 - 1950000. Why are all loop not executing?

import threading as th

x=0

class Semaphore:
    def __init__(self):
        self.__s = 1
    def wait(self):
        while(self.__s==0):
            pass
        self.__s-=1

    def signal(self):
        self.__s+=1

def increment(s):
    global x
    s.wait()
    x+=1
    s.signal()

def task1(s):
    for _ in range(1000000):
        increment(s)

def task2(s):
    for _ in range(1000000):
        increment(s)

def main():
    s = Semaphore()
    t1 = th.Thread(target=task1,name="t1",args=(s,))
    t2 = th.Thread(target=task2,name="t1",args=(s,))
    t1.start()
    t2.start()
    #Checking Synchronization
    for _ in range(10):
        print("Value of X: %d"%x)
    
    #waiting for termination of thread
    t2.join()
    t1.join()


if __name__=="__main__":
    main()
    print("X = %d"%x) #Final Output

Output:

Value of X: 5939
Value of X: 14150
Value of X: 25036
Value of X: 50490
Value of X: 54136
Value of X: 57674
Value of X: 69994
Value of X: 84912
Value of X: 94284
Value of X: 105895
X = 1801436
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Ankush K
  • 334
  • 3
  • 13

1 Answers1

0

The threads are working fine and they're completing correctly. It's your 'z' variable that's the problem.

In general using a global variable as a container for your shared memory between two threads is a bad way to go about it.

Check out this answer to see why.

I made the following changes to your code. I made 'z' the shared variable and 'x' and 'y' are data for each thread alone.

x=0
y=0
z=0

def increment1(s):
    global x,z
    s.wait()
    x+=1
    z+=1
    s.signal()

def increment2(s):
    global y,z
    s.wait()
    y+=1
    z+=1
    s.signal()

def task1(s):
    for somei in range(1000000):
        increment1(s)

def task2(s):
    for somej in range(1000000):
        increment2(s)

This is the output I got:

X = 1000000
Y = 1000000
Z = 1961404

As you can see there's nothing wrong with the threads themselves, as they're completing their execution. But the shared data Z is a little wonky. Z will change randomly each time you run the script. Hence as you can see using global variables as shared memory is a bad idea.

A much better option would be using some python supported sharing tool such as Queue provided by python's library itself. It's a multi-producer, multi-consumer message queue and helps when it comes to shared data such as the data you're using now.

Let me show you how it can be done with Queue:

import threading as th
from Queue import Queue

def task1(q):
    global x,z
    for somei in range(1000000):
        q.put(q.get() + 1)

def task2(q):
    global y,z
    for somei in range(1000000):
        q.put(q.get() + 1)

def main():
    queue = Queue()
    queue.put(0)
    t1 = th.Thread(target=task1,name="t1",args=(queue, ))
    t2 = th.Thread(target=task2,name="t1",args=(queue, ))
    t1.start()
    t2.start()
    #Checking Synchronization

    t1.join()
    t2.join()
    return queue.get()

if __name__=="__main__":
    print("Queue = %d"%main()) #Final Output

You don't even need to create a semaphore here as the Queue will automatically take care of synchronization.

The output of this final program is this:

Queue = 2000000
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459