Race conditions. The actual operation of x += 1
is roughly:
- Load the value of
x
- Compute
x + 1
- Store the computed value to
x
Except with threading, you might get preempted by the other thread after step 1 and before step 3 (whether it's before or after 2 doesn't matter). If the other thread sees the unincremented value, decrements that, then you store your incremented value before it stores the decremented value, you just dropped an increment; if they store before you do, you dropped a decrement.
You need to lock access to shared variables to ensure that the operation behaves atomically:
import threading
x=0
xlock = threading.Lock()
class a(threading.Thread):
def run(self):
global x
for i in range(1000000):
with xlock:
x+=1
class b(threading.Thread):
def run(self):
global x
for i in range(1000000):
with xlock:
x-=1
This may introduce quite a bit of overhead, so other alternatives that touch the shared variable less may be better options (at the expense of having different behaviors).