why using multiple threading to get the sum is correct?

python

Solution

Don't count on `x += 1` being thread-safe. Here is an example where it does not work (see Josiah Carlson's 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 disassemble `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 there is a `LOAD_GLOBAL` to retrieve the value of `x`, there is an `INPLACE_ADD`, and then a `STORE_GLOBAL`.

If both threads `LOAD_GLOBAL` in succession, then they might both load the same value of `x`. Then they both increment to the same number, and store the same number. So the work of one thread overwrites the work of the other. This is not thread-safe.

As you can see, the final value of `x` would be 2000000 if the program were thread-safe, but instead you almost always get a number less than 2000000.

If you add a lock, you get the "expected" answer:

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)

yields

2000000

I think the reason why the code you posted does not exhibit a problem:

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

is because your `worker`s complete so darn quickly compared to the time it takes to spawn a new thread that in practice there is no competition between threads. In Josiah Carlson's example above, each thread spends a significant amount of time in `foo` which increases the chance of thread collision.

Problem

my code is ``` 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 don't use lock to protect the shared resource,i.e. counter variable, I expect that the result is a number less than 1000, but the counter is always 1000, I don't know why. Does `counter += 1` is a atomic operation in Python? what operations in Python are atomic using GIL?

Original source