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=True to print calls to ensure output buffers are flushed.
- Removed the
join calls, 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.