Two threads print even and odd numbers between 0 to 5 alternately in Python

274 Views Asked by At

I'm trying to get the result below running 2 threads alternately. *Between 0 to 5, thread A prints even numbers and thread B prints odd numbers (I use Python 3.8.5):

A:0
B:1
A:2
B:3
A:4
B:5

So, with global variables, locks and while statements, I created the code below to try to get the result above:

import threading
lock = threading.Lock()
owner = "A"
i = 0

def test1():
    global owner, i
    while i <= 5:
        lock.acquire()
        if owner == "A":
            print(owner + ":" + str(i))
            owner = "B"
            i += 1
        lock.release()

def test2():
    global owner, i
    while i <= 5:
        lock.acquire()
        if owner == "B":
            print(owner + ":" + str(i))
            owner = "A"
            i += 1
        lock.release()

A = threading.Thread(target=test1)
B = threading.Thread(target=test2)

A.start()
B.start()

A.join()
B.join()

But, the code above got the result below with A:6. *Thread A printed even number 6:

A:0
B:1
A:2
B:3
A:4
B:5
A:6 # Here

I couldn't find any mistakes so how can I get the proper result without A:6? And, why did I get the result with A:6?

1

There are 1 best solutions below

1
ScottC On

The reason you are seeing the issue, is that the thread is still within the while loop when i moves from 5 to 6. The thread seems to obtain lock before the value is updated. i.e. The speed of release and lock is quicker than the assignment of i. So the thread goes through one more round, but the only check you have is if the owner is equal to A or B. So you have to once again have the check in place to test that i<=5.

If you had a single thread, then you would not have this issue, but because you are using two threads, you have to think of them as different entities, doing their own jobs. While i is being updated in one thread, the other thread is still spinning and checking, locking and unlocking.

The best way to visualise the thread actions is to have print statements within the lock (before the if-statement) - to see what each thread is up to.

import threading
lock = threading.Lock()
owner = "A"
i = 0

def test1():
    global owner, i
    while i <= 5:
        lock.acquire()  # Here
        if owner == "A" and i<=5:
            print(owner + ":" + str(i))
            owner = "B"
            i += 1
        lock.release()

def test2():
    global owner, i
    while i <= 5:
        lock.acquire()  # Here
        if owner == "B" and i<=5:
            print(owner + ":" + str(i))
            owner = "A"
            i += 1
        lock.release()

A = threading.Thread(target=test1)
B = threading.Thread(target=test2)

A.start()
B.start()

A.join()
B.join()

OUTPUT:

A:0
B:1
A:2
B:3
A:4
B:5