Please see the below code snippet, which fails to get past the first run locally with Python 3.10.
When I try to join
all the threads within the built-in all
function, always one of the threads is still alive.
What is the issue here, why is a race condition happening?
import time
from threading import Thread
def expose_race_condition(run_index: int) -> None:
threads: list[Thread] = [
Thread(target=time.sleep, args=(1,)) for _ in range(10)
]
for thread in threads:
thread.start()
# RACE CONDITION IS HERE
assert not all(thread.join() for thread in threads)
for thread_index, thread in enumerate(threads):
# Uncommenting the below line defeats the race condition
# assert not thread.join()
assert (
not thread.is_alive()
), f"Worker {thread_index} in run {run_index} was still alive."
print(f"Completed run {run_index}")
if __name__ == "__main__":
for i in range(10_000):
expose_race_condition(i)
Sorry, I don't understand what you're trying to do. This line, for example:
assert not all(thread.join() for thread in threads)
just doesn't make sense. .join()
always returns None
, so it's the same as
assert not all(None for thread in threads)
except that it has the side effect of joining threads. all()
short-circuits the first time it sees a False
value, which None
is, so only the first .join()
is actually called. all(...)
returns False
, so not all(...)
returns True
, so the assert
succeeds. It's like:
threads[0].join()
assert True
Short course: any code paying any attention to the value thread.join()
returns is probably flat-out broken, because None
is the only value it will ever see.