Why is using multiple threads to get the amount right?

my code

import threading

counter = 0

def worker():
    global counter
    counter += 1

if __name__ == "__main__":
    threads = []
    for i in range(1000):
        t = threading.Thread(target = worker)
        threads.append(t)
        t.start()
    for t in threads:
        t.join()

    print counter

because I do not use locking to protect the shared resource, i.e. counter variable, I expect the result to be less than 1000, but the counter is always 1000, I don’t know why. Is counter += 1atomic operation in Python?

What operations in Python are atomic using GIL?

+5
source share
1 answer

Do not expect to x += 1be thread safe. Here is an example where it does not work (see Josiah Carlson Comment):

import threading
x = 0
def foo():
    global x
    for i in xrange(1000000):
        x += 1
threads = [threading.Thread(target=foo), threading.Thread(target=foo)]
for t in threads:
    t.daemon = True
    t.start()
for t in threads:
    t.join()
print(x)

If you make out foo:

In [80]: import dis

In [81]: dis.dis(foo)
  4           0 SETUP_LOOP              30 (to 33)
              3 LOAD_GLOBAL              0 (xrange)
              6 LOAD_CONST               1 (1000000)
              9 CALL_FUNCTION            1
             12 GET_ITER            
        >>   13 FOR_ITER                16 (to 32)
             16 STORE_FAST               0 (i)

  5          19 LOAD_GLOBAL              1 (x)
             22 LOAD_CONST               2 (1)
             25 INPLACE_ADD         
             26 STORE_GLOBAL             1 (x)
             29 JUMP_ABSOLUTE           13
        >>   32 POP_BLOCK           
        >>   33 LOAD_CONST               0 (None)
             36 RETURN_VALUE        

You see that to get the value xexists LOAD_GLOBAL, is INPLACE_ADD, and then a STORE_GLOBAL.

LOAD_GLOBAL , x. . , . .

, x 2000000, , 2000000.


, "" :

import threading
lock = threading.Lock()
x = 0
def foo():
    global x
    for i in xrange(1000000):
        with lock:
            x += 1
threads = [threading.Thread(target=foo), threading.Thread(target=foo)]
for t in threads:
    t.daemon = True
    t.start()
for t in threads:
    t.join()
print(x)

2000000

, , , :

for i in range(1000):
    t = threading.Thread(target = worker)
    threads.append(t)
    t.start()

, worker , , . Josiah Carlson foo, .

+8

All Articles