@@ -5508,6 +5508,115 @@ can be used interchangeably to index the same dictionary entry.
55085508 .. versionchanged :: 3.8
55095509 Dictionaries are now reversible.
55105510
5511+ .. admonition :: Thread safety
5512+
5513+ The following operations and function are lock-free and
5514+ :term: `atomic <atomic operation> `.
5515+
5516+ .. code-block ::
5517+ :class: good
5518+
5519+ d[key] # dict.__getitem__
5520+ d.get(key) # dict.get
5521+ key in d # dict.__contains__
5522+ len(d) # dict.__len__
5523+
5524+ These operations may compare keys using :meth: `~object.__eq__ `, which can
5525+ execute arbitrary Python code. During such comparisons, the dictionary may
5526+ be modified by another thread. For built-in types like :class: `str `,
5527+ :class: `int `, and :class: `float `, that implement :meth: `~object.__eq__ ` in C,
5528+ the underlying lock is not released during comparisons and this is not a
5529+ concern.
5530+
5531+ All other operations from here on hold the per-object lock.
5532+
5533+ Writing or removing a single item is safe to call from multiple threads
5534+ and will not corrupt the dictionary:
5535+
5536+ .. code-block ::
5537+ :class: good
5538+
5539+ d[key] = value # write
5540+ del d[key] # delete
5541+ d.pop(key) # remove and return
5542+ d.popitem() # remove and return last item
5543+ d.setdefault(key, v) # insert if missing
5544+
5545+ These operations also compare keys, so the same :meth: `~object.__eq__ `
5546+ considerations as above apply.
5547+
5548+ The following operations return new objects and hold the per-object lock
5549+ for the duration:
5550+
5551+ .. code-block ::
5552+ :class: good
5553+
5554+ d.copy() # returns a shallow copy of the dictionary
5555+ d | other # merges two dicts into a new dict
5556+ d.keys() # returns a new dict_keys view object
5557+ d.values() # returns a new dict_values view object
5558+ d.items() # returns a new dict_items view object
5559+
5560+ The :meth: `~dict.clear ` method holds the lock for its duration. Other
5561+ threads cannot observe elements being removed.
5562+
5563+ The following operations lock both dictionaries. For :meth: `~dict.update `
5564+ and ``|= ``, this applies only when the other operand is a :class: `dict `
5565+ that uses the standard dict iterator (but not subclasses that override
5566+ iteration). For equality comparison, this applies to :class: `dict ` and
5567+ its subclasses:
5568+
5569+ .. code-block ::
5570+ :class: good
5571+
5572+ d.update(other_dict) # both locked when other_dict is a dict
5573+ d |= other_dict # both locked when other_dict is a dict
5574+ d == other_dict # both locked for dict and subclasses
5575+
5576+ The equality comparison also compares values using :meth: `~object.__eq__ `,
5577+ so for non-built-in types the lock may be released during comparison.
5578+
5579+ :meth: `~dict.fromkeys ` locks both the new dictionary and the iterable
5580+ when the iterable is exactly a :class: `dict `, :class: `set `, or
5581+ :class: `frozenset ` (not subclasses):
5582+
5583+ .. code-block ::
5584+ :class: good
5585+
5586+ dict.fromkeys(a_dict) # locks both
5587+ dict.fromkeys(a_set) # locks both
5588+ dict.fromkeys(a_frozenset) # locks both
5589+
5590+ When updating from a non-dict iterable, only the target dictionary is
5591+ locked. The iterable may be concurrently modified by another thread:
5592+
5593+ .. code-block ::
5594+ :class: maybe
5595+
5596+ d.update(iterable) # iterable is not a dict
5597+ d |= iterable # iterable is not a dict
5598+ dict.fromkeys(iterable) # iterable is not a dict/set/frozenset
5599+
5600+ Operations that involve multiple accesses, as well as iteration, are never
5601+ atomic:
5602+
5603+ .. code-block ::
5604+ :class: bad
5605+
5606+ # NOT atomic: read-modify-write
5607+ d[key] = d[key] + 1
5608+
5609+ # NOT atomic: check-then-act
5610+ if key in d:
5611+ del d[key]
5612+
5613+ # NOT thread-safe: iteration while modifying
5614+ for key in d:
5615+ process(key) # another thread may modify d
5616+
5617+ Consider external synchronization when sharing :class: `dict ` instances
5618+ across threads. See :ref: `freethreading-python-howto ` for more information.
5619+
55115620
55125621.. seealso ::
55135622 :class: `types.MappingProxyType ` can be used to create a read-only view
0 commit comments