aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Doc/library/threading.rst
diff options
context:
space:
mode:
Diffstat (limited to 'Doc/library/threading.rst')
-rw-r--r--Doc/library/threading.rst129
1 files changed, 119 insertions, 10 deletions
diff --git a/Doc/library/threading.rst b/Doc/library/threading.rst
index d948493c210..249c0a5cb03 100644
--- a/Doc/library/threading.rst
+++ b/Doc/library/threading.rst
@@ -260,23 +260,132 @@ All of the methods described below are executed atomically.
Thread-Local Data
-----------------
-Thread-local data is data whose values are thread specific. To manage
-thread-local data, just create an instance of :class:`local` (or a
-subclass) and store attributes on it::
+Thread-local data is data whose values are thread specific. If you
+have data that you want to be local to a thread, create a
+:class:`local` object and use its attributes::
- mydata = threading.local()
- mydata.x = 1
+ >>> mydata = local()
+ >>> mydata.number = 42
+ >>> mydata.number
+ 42
-The instance's values will be different for separate threads.
+You can also access the :class:`local`-object's dictionary::
+
+ >>> mydata.__dict__
+ {'number': 42}
+ >>> mydata.__dict__.setdefault('widgets', [])
+ []
+ >>> mydata.widgets
+ []
+
+If we access the data in a different thread::
+
+ >>> log = []
+ >>> def f():
+ ... items = sorted(mydata.__dict__.items())
+ ... log.append(items)
+ ... mydata.number = 11
+ ... log.append(mydata.number)
+
+ >>> import threading
+ >>> thread = threading.Thread(target=f)
+ >>> thread.start()
+ >>> thread.join()
+ >>> log
+ [[], 11]
+
+we get different data. Furthermore, changes made in the other thread
+don't affect data seen in this thread::
+
+ >>> mydata.number
+ 42
+
+Of course, values you get from a :class:`local` object, including their
+:attr:`~object.__dict__` attribute, are for whatever thread was current
+at the time the attribute was read. For that reason, you generally
+don't want to save these values across threads, as they apply only to
+the thread they came from.
+
+You can create custom :class:`local` objects by subclassing the
+:class:`local` class::
+
+ >>> class MyLocal(local):
+ ... number = 2
+ ... def __init__(self, /, **kw):
+ ... self.__dict__.update(kw)
+ ... def squared(self):
+ ... return self.number ** 2
+
+This can be useful to support default values, methods and
+initialization. Note that if you define an :py:meth:`~object.__init__`
+method, it will be called each time the :class:`local` object is used
+in a separate thread. This is necessary to initialize each thread's
+dictionary.
+
+Now if we create a :class:`local` object::
+
+ >>> mydata = MyLocal(color='red')
+
+we have a default number::
+
+ >>> mydata.number
+ 2
+
+an initial color::
+
+ >>> mydata.color
+ 'red'
+ >>> del mydata.color
+
+And a method that operates on the data::
+
+ >>> mydata.squared()
+ 4
+
+As before, we can access the data in a separate thread::
+
+ >>> log = []
+ >>> thread = threading.Thread(target=f)
+ >>> thread.start()
+ >>> thread.join()
+ >>> log
+ [[('color', 'red')], 11]
+
+without affecting this thread's data::
+
+ >>> mydata.number
+ 2
+ >>> mydata.color
+ Traceback (most recent call last):
+ ...
+ AttributeError: 'MyLocal' object has no attribute 'color'
+
+Note that subclasses can define :term:`__slots__`, but they are not
+thread local. They are shared across threads::
+
+ >>> class MyLocal(local):
+ ... __slots__ = 'number'
+
+ >>> mydata = MyLocal()
+ >>> mydata.number = 42
+ >>> mydata.color = 'red'
+
+So, the separate thread::
+
+ >>> thread = threading.Thread(target=f)
+ >>> thread.start()
+ >>> thread.join()
+
+affects what we see::
+
+ >>> mydata.number
+ 11
.. class:: local()
A class that represents thread-local data.
- For more details and extensive examples, see the documentation string of the
- :mod:`!_threading_local` module: :source:`Lib/_threading_local.py`.
-
.. _thread-objects:
@@ -452,7 +561,7 @@ since it is impossible to detect the termination of alien threads.
of :term:`Python finalization <interpreter shutdown>` :meth:`!join`
raises a :exc:`PythonFinalizationError`.
- .. versionchanged:: next
+ .. versionchanged:: 3.14
May raise :exc:`PythonFinalizationError`.