Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is the += operator thread-safe in Python?

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():   c += 1  def decrement():   c -= 1 

Is this code thread safe?

If not, may I understand why it is not thread safe, and what kind of statements usually lead to non-thread-safe operations.

If it is thread-safe, how can I make it explicitly non-thread-safe?

like image 747
nubela Avatar asked Nov 11 '09 19:11

nubela


People also ask

Are Python threads safe?

Python is not by its self thread safe. But there are moves to change this: NoGil, etc. Removing the GIL does not make functions thread-safe.

What does thread-safe mean Python?

Thread safety is a computer programming concept applicable to multi-threaded code. Thread-safe code only manipulates shared data structures in a manner that ensures that all threads behave properly and fulfill their design specifications without unintended interaction.

Is the ++ operator thread-safe?

These operators are not thread-safe. Imagine two threads that increment a variable. If they do the operation serially, the variable ends up correctly incremented twice.

Is operator new thread-safe?

The C++ new and delete operators are thread safe, but this means that a thread may have to wait for a lock on these operations. Once memory is obtained for a thread, the thread_alloc memory allocator keeps that memory available for the thread so that it can be re-used without waiting for a lock.


2 Answers

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 
like image 180
Glenn Maynard Avatar answered Sep 22 '22 13:09

Glenn Maynard


(note: you would need global c in each function to make your code work.)

Is this code thread safe?

No. Only a single bytecode instruction is ‘atomic’ in CPython, and a += may not result in a single opcode, even when the values involved are simple integers:

>>> c= 0 >>> def inc(): ...     global c ...     c+= 1  >>> import dis >>> dis.dis(inc)    3           0 LOAD_GLOBAL              0 (c)               3 LOAD_CONST               1 (1)               6 INPLACE_ADD                        7 STORE_GLOBAL             0 (c)              10 LOAD_CONST               0 (None)              13 RETURN_VALUE         

So one thread could get to index 6 with c and 1 loaded, give up the GIL and let another thread in, which executes an inc and sleeps, returning the GIL to the first thread, which now has the wrong value.

In any case, what's atomic is an implementation detail which you shouldn't rely on. Bytecodes may change in future versions of CPython, and the results will be totally different in other implementations of Python that do not rely on a GIL. If you need thread safety, you need a locking mechanism.

like image 24
bobince Avatar answered Sep 24 '22 13:09

bobince