Reputation: 11
Im working on a problem that requires multiple threads to change a shared piece of memory. In order to gain some understanding about how this works in python, I developed the following code to see if I can get a race condition:
import threading
class A:
def __init__(self):
self.val = 0
def increment(a):
for _ in range(10):
a.val += 1
def main():
a = A()
threads = [threading.Thread(target=increment, args=(a,)) for _ in range(4)]
for t in threads:
t.start()
for t in threads:
t.join()
print(a.val)
if __name__ == '__main__':
main()
Pretty simple stuff. Each thread should be trying to increment a.val at the same time, which should result in a race condition, since the value is not protected with locks. However, this is not the case. Every time I run this code, the printed value for a.val is 40. Since there are four threads and each one increments a.val 10 times, this is the correct answer. Can anyone explain why this does not result in a race condition?
Upvotes: 1
Views: 218
Reputation: 458
I changed to for _ in range(100000)
and got different answers each time.
Otherwise, I guess the computation completes before the next thread is spun up? Indeed, if I add time.sleep(1)
before t.start()
, the result is the "correct" one.
==
This is the solution (but not the answer to the question) proposed in the comments:
class A:
def __init__(self):
self.val = 0
self.lock = threading.Lock()
def __iadd__(self, y):
with self.lock:
self.val += y
return self
def increment(a: A):
for _ in range(100000):
a += 1
Upvotes: 0
Reputation: 35636
It does result in a race condition. Your current range is just insufficient to evaluate this. Effectively the first started thread has completed before the next thread can be started.
The specific number that will take long enough that this is no longer the case will depend on your computer resources, for me, modifying the increment function to a larger bound did result in val being a different value on each run.
def increment(a):
for _ in range(1_000_000):
a.val += 1
Upvotes: 1