Thread-safe way to add a key to a dict only if it isn't already there?
Chris Angelico
rosuav at gmail.com
Sat Jul 7 21:15:17 EDT 2018
On Sun, Jul 8, 2018 at 11:04 AM, Steven D'Aprano
<steve+comp.lang.python at pearwood.info> wrote:
>> The only thing Python should guarantee is that the data structures stay
>> "coherent" under race conditions. In other words, there cannot be a
>> segmentation fault. For example, if two threads executed this code in
>> parallel:
>>
>> global i
>> i = 1
>> i += 1
>>
>> a legal end result could be that i contains the string "impossible".
>
> That wouldn't be coherent. The only coherent results are that i could
> equal either 2 or 3:
>
> Possibility 1:
>
> i = 1 # thread 1
> i += 1 # thread 1
> i = 1 # thread 2
> i += 1 # thread 2
> assert i == 2
>
> Possibility 2:
>
> i = 1 # thread 1
> i = 1 # thread 2
> i += 1 # thread 1
> i += 1 # thread 2
> assert i == 3
>
>
> Executing statements out of order is impossible, even in threaded code.
> Redefining the meaning of the integer literal 1 is impossible (ignoring
> unsupported shenanigans with ctypes). Monkey-patching the int __iadd__
> method is impossible. So there is no coherent way to get a result of
> "impossible" from just adding 1 to 1 in any coherent implementation of
> Python.
Python threads don't switch only between lines of code, so the actual
interaction is a bit more complicated than you say. In CPython, the
increment operation is:
3 0 LOAD_GLOBAL 0 (i)
2 LOAD_CONST 1 (1)
4 INPLACE_ADD
6 STORE_GLOBAL 0 (i)
A context switch could happen between any pair of statements. In this
particular example, the end result doesn't change - coherent results
are 2 and 3, nothing else - but in other situations, there may be
times when the separate steps might be significant. For instance, if
you replace "i += 1" with "i += i", to double the value, you'll get
this:
3 0 LOAD_GLOBAL 0 (i)
2 LOAD_GLOBAL 0 (i)
4 INPLACE_ADD
6 STORE_GLOBAL 0 (i)
and that could potentially have both of them load the initial value,
then one of them runs to completion, and then the other loads the
result - so it'll add 1 and 2 and have a result of 3, rather than 2 or
4.
But you're absolutely right that there are only a small handful of
plausible results, even with threading involved.
ChrisA
More information about the Python-list
mailing list