Is the += operator thread-safe in Python?

后端 未结 8 1569
萌比男神i
萌比男神i 2020-11-27 13:45

I want to create a non-thread-safe chunk of code for experimentation, and those are the functions that 2 threads are going to call.

c = 0

def increment():
          


        
8条回答
  •  难免孤独
    2020-11-27 14:17

    No, this code is absolutely, demonstrably not threadsafe.

    import threading
    
    i = 0
    
    def test():
        global i
        for x in range(100000):
            i += 1
    
    threads = [threading.Thread(target=test) for t in range(10)]
    for t in threads:
        t.start()
    
    for t in threads:
        t.join()
    
    assert i == 1000000, i
    

    fails consistently.

    i += 1 resolves to four opcodes: load i, load 1, add the two, and store it back to i. The Python interpreter switches active threads (by releasing the GIL from one thread so another thread can have it) every 100 opcodes. (Both of these are implementation details.) The race condition occurs when the 100-opcode preemption happens between loading and storing, allowing another thread to start incrementing the counter. When it gets back to the suspended thread, it continues with the old value of "i" and undoes the increments run by other threads in the meantime.

    Making it threadsafe is straightforward; add a lock:

    #!/usr/bin/python
    import threading
    i = 0
    i_lock = threading.Lock()
    
    def test():
        global i
        i_lock.acquire()
        try:
            for x in range(100000):
                i += 1
        finally:
            i_lock.release()
    
    threads = [threading.Thread(target=test) for t in range(10)]
    for t in threads:
        t.start()
    
    for t in threads:
        t.join()
    
    assert i == 1000000, i
    

提交回复
热议问题