You need it. Although multithreading works differently in Python, due to Global Interpreter Lock, operations that are not atomic in Python bytecode still require locking.
In this case, you can check the bytecode for your test ( dis.dis(test) ) function:
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)
As you can see, the increment is 2xload, update, store at the bytecode level, so this will not work. An increment is actually 4 separate operations that you must protect to ensure that 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)
source share