GeezerVisonThe World Wide Web, restored to 800 pixels of glory. |
| Home|About|Gaming cabinet|Archives|How it works|Starter sites |
GeezerVison is showing docs.python.org — Thread Safety Guarantees Thread Safety Guarantees¶This page documents thread-safety guarantees for built-in types in Python’s free-threaded build. The guarantees described here apply when using Python with the GIL disabled (free-threaded mode). When the GIL is enabled, most operations are implicitly serialized. For general guidance on writing thread-safe code in free-threaded Python, see Python support for free threading. Thread safety levels¶The C API documentation uses the following levels to describe the thread safety guarantees of each function. The levels are listed from least to most safe. Incompatible¶A function or operation that cannot be made safe for concurrent use even with external synchronization. Incompatible code typically accesses global state in an unsynchronized way and must only be called from a single thread throughout the program’s lifetime. Example: a function that modifies process-wide state such as signal handlers or environment variables, where concurrent calls from any threads, even with external locking, can conflict with the runtime or other libraries. Compatible¶A function or operation that is safe to call from multiple threads provided the caller supplies appropriate external synchronization, for example by holding a lock for the duration of each call. Without such synchronization, concurrent calls may produce race conditions or data races. Example: a function that reads from or writes to an object whose internal state is not protected by a lock. Callers must ensure that no two threads access the same object at the same time. Safe on distinct objects¶A function or operation that is safe to call from multiple threads without external synchronization, as long as each thread operates on a different object. Two threads may call the function at the same time, but they must not pass the same object (or objects that share underlying state) as arguments. Example: a function that modifies fields of a struct using non-atomic writes. Two threads can each call the function on their own struct instance safely, but concurrent calls on the same instance require external synchronization. Safe on shared objects¶A function or operation that is safe for concurrent use on the same object. The implementation uses internal synchronization (such as per-object locks or critical sections) to protect shared mutable state, so callers do not need to supply their own locking. Example: Atomic¶A function or operation that appears atomic with respect to other threads - it executes instantaneously from the perspective of other threads. This is the strongest form of thread safety. Example: Thread safety for list objects¶Reading a single element from a lst[i] # list.__getitem__ The following methods traverse the list and use atomic reads of each item to perform their function. That means that they may return results affected by concurrent modifications: item in lst lst.index(item) lst.count(item) All of the above operations avoid acquiring per-object locks. They do not block concurrent modifications. Other operations that hold a lock will not block these from observing intermediate states. All other operations from here on block using the per-object lock. Writing a single item via The following operations return new objects and appear atomic to other threads: lst1 + lst2 # concatenates two lists into a new list x * lst # repeats lst x times into a new list lst.copy() # returns a shallow copy of the list The following methods that only operate on a single element with no shifting required are atomic: lst.append(x) # append to the end of the list, no shifting required lst.pop() # pop element from the end of the list, no shifting required The The The following operations may allow lock-free operations to observe intermediate states since they modify multiple elements in place: lst.insert(idx, item) # shifts elements lst.pop(idx) # idx not at the end of the list, shifts elements lst *= x # copies elements in place The
Similarly, assigning to a list slice with Operations that involve multiple accesses, as well as iteration, are never atomic. For example: # NOT atomic: read-modify-write lst[i] = lst[i] + 1 # NOT atomic: check-then-act if lst: item = lst.pop() # NOT thread-safe: iteration while modifying for item in lst: process(item) # another thread may modify lst Consider external synchronization when sharing Thread safety for dict objects¶Creating a dictionary with the The following operations and functions are lock-free and atomic. d[key] # dict.__getitem__ d.get(key) # dict.get key in d # dict.__contains__ len(d) # dict.__len__ All other operations from here on hold the per-object lock. Writing or removing a single item is safe to call from multiple threads and will not corrupt the dictionary: d[key] = value # write del d[key] # delete d.pop(key) # remove and return d.popitem() # remove and return last item d.setdefault(key, v) # insert if missing These operations may compare keys using The following operations return new objects and hold the per-object lock for the duration of the operation: d.copy() # returns a shallow copy of the dictionary d | other # merges two dicts into a new dict d.keys() # returns a new dict_keys view object d.values() # returns a new dict_values view object d.items() # returns a new dict_items view object The The following operations lock both dictionaries. For d.update(other_dict) # both locked when other_dict is a dict d |= other_dict # both locked when other_dict is a dict d == other_dict # both locked for dict and subclasses All comparison operations also compare values using
dict.fromkeys(a_dict) # locks both dict.fromkeys(a_set) # locks both dict.fromkeys(a_frozenset) # locks both When updating from a non-dict iterable, only the target dictionary is locked. The iterable may be concurrently modified by another thread: d.update(iterable) # iterable is not a dict: only d locked d |= iterable # iterable is not a dict: only d locked dict.fromkeys(iterable) # iterable is not a dict/set/frozenset: only result locked Operations that involve multiple accesses, as well as iteration, are never atomic: # NOT atomic: read-modify-write d[key] = d[key] + 1 # NOT atomic: check-then-act (TOCTOU) if key in d: del d[key] # NOT thread-safe: iteration while modifying for key, value in d.items(): process(key) # another thread may modify d To avoid time-of-check to time-of-use (TOCTOU) issues, use atomic operations or handle exceptions: # Use pop() with default instead of check-then-delete d.pop(key, None) # Or handle the exception try: del d[key] except KeyError: pass To safely iterate over a dictionary that may be modified by another thread, iterate over a copy: # Make a copy to iterate safely for key, value in d.copy().items(): process(key) Consider external synchronization when sharing Thread safety for set objects¶The The following read operation is lock-free. It does not block concurrent modifications and may observe intermediate states from operations that hold the per-object lock: elem in s # set.__contains__ This operation may compare elements using All other operations from here on hold the per-object lock. Adding or removing a single element is safe to call from multiple threads and will not corrupt the set: s.add(elem) # add element s.remove(elem) # remove element, raise if missing s.discard(elem) # remove element if present s.pop() # remove and return arbitrary element These operations also compare elements, so the same The The The following operations only accept s |= other # other must be set/frozenset s &= other # other must be set/frozenset s -= other # other must be set/frozenset s ^= other # other must be set/frozenset s & other # other must be set/frozenset s | other # other must be set/frozenset s - other # other must be set/frozenset s ^ other # other must be set/frozenset
The update variants of the above methods also have some differences between them:
The following methods always try to lock both objects: s.isdisjoint(other) # both locked s.issubset(other) # both locked s.issuperset(other) # both locked Operations that involve multiple accesses, as well as iteration, are never atomic: # NOT atomic: check-then-act if elem in s: s.remove(elem) # NOT thread-safe: iteration while modifying for elem in s: process(elem) # another thread may modify s Consider external synchronization when sharing Thread safety for bytearray objects¶
Thread safety for memoryview objects¶
The memoryview implementation uses atomic operations to track its own
exports in the free-threaded build. Creating and
releasing a memoryview are thread-safe. Attribute access (e.g.,
However, the actual data accessed through the memoryview is owned by the underlying object. Concurrent access to this data is only safe if the underlying object supports it:
# NOT safe: concurrent writes to the same buffer data = bytearray(1000) view = memoryview(data) # Thread 1: view[0:500] = b'x' * 500 # Thread 2: view[0:500] = b'y' * 500 # Safe: use a lock for concurrent access import threading lock = threading.Lock() data = bytearray(1000) view = memoryview(data) with lock: view[0:500] = b'x' * 500 Resizing or reallocating the underlying object (such as calling
© Copyright 2001 Python Software Foundation.
This page is licensed under the Python Software Foundation License Version 2. Examples, recipes, and other code in the documentation are additionally licensed under the Zero Clause BSD License. See History and License for more information. The Python Software Foundation is a non-profit corporation. Please donate. Last updated on Nov 12, 2026 (08:53 UTC). Found a bug? Created using Sphinx 9.1.1. |
| You are visitor 01402503 — GeezerVison does not store the pages you look up. Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required. |