What is "thread local storage" in Python, and why do I need it? What is "thread local storage" in Python, and why do I need it? multithreading multithreading

What is "thread local storage" in Python, and why do I need it?


In Python, everything is shared, except for function-local variables (because each function call gets its own set of locals, and threads are always separate function calls.) And even then, only the variables themselves (the names that refer to objects) are local to the function; objects themselves are always global, and anything can refer to them.The Thread object for a particular thread is not a special object in this regard. If you store the Thread object somewhere all threads can access (like a global variable) then all threads can access that one Thread object. If you want to atomically modify anything that another thread has access to, you have to protect it with a lock. And all threads must of course share this very same lock, or it wouldn't be very effective.

If you want actual thread-local storage, that's where threading.local comes in. Attributes of threading.local are not shared between threads; each thread sees only the attributes it itself placed in there. If you're curious about its implementation, the source is in _threading_local.py in the standard library.


Consider the following code:

#/usr/bin/env pythonfrom time import sleepfrom random import randomfrom threading import Thread, localdata = local()def bar():    print("I'm called from", data.v)def foo():    bar()class T(Thread):    def run(self):        sleep(random())        data.v = self.getName()   # Thread-1 and Thread-2 accordingly        sleep(1)        foo()
 >> T().start(); T().start()I'm called from Thread-2I'm called from Thread-1 

Here threading.local() is used as a quick and dirty way to pass some data from run() to bar() without changing the interface of foo().

Note that using global variables won't do the trick:

#/usr/bin/env pythonfrom time import sleepfrom random import randomfrom threading import Threaddef bar():    global v    print("I'm called from", v)def foo():    bar()class T(Thread):    def run(self):        global v        sleep(random())        v = self.getName()   # Thread-1 and Thread-2 accordingly        sleep(1)        foo()
 >> T().start(); T().start()I'm called from Thread-2I'm called from Thread-2 

Meanwhile, if you could afford passing this data through as an argument of foo() - it would be a more elegant and well-designed way:

from threading import Threaddef bar(v):    print("I'm called from", v)def foo(v):    bar(v)class T(Thread):    def run(self):        foo(self.getName())

But this is not always possible when using third-party or poorly designed code.


You can create thread local storage using threading.local().

>>> tls = threading.local()>>> tls.x = 4 >>> tls.x4

Data stored to the tls will be unique to each thread which will help ensure that unintentional sharing does not occur.