简体   繁体   中英

Do I need a lock when using python multi-thread?

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 ?

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)

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.)

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM