'Print odd and even numbers in two threads
I am learning threads in Python and tried a program to print odd and even numbers in sequence using two separate threads. Below is the program. The program runs correctly but it does not exit. Can someone please point out the mistake I have done?
import threading
odd_event = threading.Event()
even_event = threading.Event()
def printEven(n):
for i in range(0, n, 2):
print(i)
odd_event.set()
even_event.clear()
even_event.wait()
def printOdd(n):
odd_event.wait()
for i in range(1, n, 2):
print(i)
even_event.set()
odd_event.clear()
odd_event.wait()
if __name__ == "__main__":
n = 99
t1 = threading.Thread(target=printEven, args=(n,))
t2 = threading.Thread(target=printOdd, args=(n,))
t1.start()
t2.start()
t1.join()
t2.join()
Solution 1:[1]
Adding on @Roland's response, you could also check for when the event isn't supposed to wait so you don't call the wait function when you're not supposed to. For example, with n=99 - though you can adapt the approach for whichever use case you want - the printEven could look like:
def printEven(n):
for i in range(0, n, 2):
print(i)
odd_event.set()
even_event.clear()
if i != n-1:
even_event.wait()
else: # here, we know there won't be any even number to print, so we don't need to wait
print("don't wait")
Solution 2:[2]
The problem
When I changed your code slightly:
def printEven(n):
for i in range(0, n, 2):
print(i)
odd_event.set()
even_event.clear()
print("waiting for even event")
even_event.wait()
print("printEven finished")
def printOdd(n):
odd_event.wait()
for i in range(1, n, 2):
print(i)
even_event.set()
odd_event.clear()
print("waiting for odd event")
odd_event.wait()
print("printOdd finished")
This is what I saw:
95
waiting for odd event
96
waiting for even event
97
waiting for odd event
98
waiting for even event
printOdd finished
What happens is that printEven is waiting for an event that will never happen, because printOdd has already exited.
Solution
Add a timeout to the wait in the for-loops:
import threading
odd_event = threading.Event()
even_event = threading.Event()
TIMEOUT = 0.1
def printEven(n):
for i in range(0, n, 2):
print(i, flush=True)
odd_event.set()
even_event.clear()
# print("waiting for even event", flush=True)
even_event.wait(timeout=TIMEOUT)
# print("printEven finished", flush=True)
def printOdd(n):
odd_event.wait()
for i in range(1, n, 2):
print(i, flush=True)
even_event.set()
odd_event.clear()
# print("waiting for odd event", flush=True)
odd_event.wait(timeout=TIMEOUT)
# print("printOdd finished", flush=True)
if __name__ == "__main__":
n = 99
t1 = threading.Thread(target=printEven, args=(n,), daemon=False)
t2 = threading.Thread(target=printOdd, args=(n,), daemon=False)
t1.start()
t2.start()
Of note:
- Added
flush=Truetoprintcalls to ensure output buffers are flushed. - Removed the
joincalls, they are not needed in this case. The main thread is a non-daemon thread, and the new threads inherit this property. According to the documentation:
The entire Python program exits when no alive non-daemon threads are left.
So when the print threads have exited, the main thread will do so as well.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Emmanuel Murairi |
| Solution 2 |
