Reputation: 20470
Think about this code:
#!/usr/bin/env python
from threading import Thread
count = 0
def test():
global count
for i in range(10):
count = count + 1
if __name__ == '__main__':
for i in range(1000):
Thread(target = test).start()
print count
I use multiple threads, but the result is always correct. Does that mean I could use python threads without a lock when implementing something like a visitor counter ?
Upvotes: 4
Views: 864
Reputation: 76886
You do need one. Although multithreading works differently in Python, due to the Global Interpreter Lock, operations that are not atomic in Python-bytecode will still need locking.
In you case, you can inspect the bytecode for your function test
(dis.dis(test)
):
3 0 SETUP_LOOP 30 (to 33)
3 LOAD_GLOBAL 0 (range)
6 LOAD_CONST 1 (1000)
9 CALL_FUNCTION 1
12 GET_ITER
>> 13 FOR_ITER 16 (to 32)
16 STORE_FAST 0 (i)
4 19 LOAD_GLOBAL 1 (count) # start of increment
22 LOAD_CONST 2 (1)
25 BINARY_ADD
26 STORE_GLOBAL 1 (count) # end of increment
29 JUMP_ABSOLUTE 13
>> 32 POP_BLOCK
>> 33 LOAD_CONST 0 (None)
36 RETURN_VALUE
As you can see, the increment is a 2xload, update, store on bytecode-level, so this wont work. The increment is actually 4 separate operations, which you must protect to ensure they are not interrupted.
In your example the problem remains even if you use count += 1
, as the bytecode shows:
4 19 LOAD_GLOBAL 1 (count)
22 LOAD_CONST 2 (1)
25 INPLACE_ADD
26 STORE_GLOBAL 1 (count)
Upvotes: 6
Reputation: 91149
You wouldn't need a lock if you just did assignments.
But as you do count = count + 1
, something can happen between each of reading out count
, adding 1
and writing to count
.
Even using count += 1
wouldn't solve this problem, as this involves an assignment as well. (As the inplace operations involve an assignment under the hood as well, the situation is the same.)
Upvotes: 1
Reputation: 597
Definitely you should use Lock. You have got a correct answer in such simple case. Try to make it for range(100000) in main. You will see the problem. In my machine the result is 999960, but it is random result. Errors will arise depending on system load and so on.
Upvotes: 0