12.6 保存线程的状态信息
最后更新于:2022-04-01 15:39:16
## 问题
You need to store state that’s specific to the currently executing thread and not visibleto other threads.
## 解决方案
Sometimes in multithreaded programs, you need to store data that is only specific tothe currently executing thread. To do this, create a thread-local storage object usingthreading.local(). Attributes stored and read on this object are only visible to theexecuting thread and no others.As an interesting practical example of using thread-local storage, consider the LazyConnection context-manager class that was first defined in Recipe 8.3. Here is a slightlymodified version that safely works with multiple threads:
from socket import socket, AF_INET, SOCK_STREAMimport threading
class LazyConnection:def __init__(self, address, family=AF_INET, type=SOCK_STREAM):self.address = addressself.family = AF_INETself.type = SOCK_STREAMself.local = threading.local()def __enter__(self):if hasattr(self.local, ‘sock'):raise RuntimeError(‘Already connected')
self.local.sock = socket(self.family, self.type)self.local.sock.connect(self.address)return self.local.sock
def __exit__(self, exc_ty, exc_val, tb):self.local.sock.close()del self.local.sock
In this code, carefully observe the use of the self.local attribute. It is initialized as aninstance of threading.local(). The other methods then manipulate a socket that’sstored as self.local.sock. This is enough to make it possible to safely use an instanceof LazyConnection in multiple threads. For example:
from functools import partialdef test(conn):
> with conn as s:> s.send(b'GET /index.html HTTP/1.0rn')s.send(b'Host: www.python.orgrn')
> s.send(b'rn')resp = b'‘.join(iter(partial(s.recv, 8192), b'‘))
> print(‘Got {} bytes'.format(len(resp)))
if __name__ == ‘__main__':
conn = LazyConnection((‘www.python.org', 80))
t1 = threading.Thread(target=test, args=(conn,))t2 = threading.Thread(target=test, args=(conn,))t1.start()t2.start()t1.join()t2.join()
The reason it works is that each thread actually creates its own dedicated socket con‐nection (stored as self.local.sock). Thus, when the different threads perform socketoperations, they don’t interfere with one another as they are being performed on dif‐ferent sockets.
## 讨论
Creating and manipulating thread-specific state is not a problem that often arises inmost programs. However, when it does, it commonly involves situations where an objectbeing used by multiple threads needs to manipulate some kind of dedicated systemresource, such as a socket or file. You can’t just have a single socket object shared byeveryone because chaos would ensue if multiple threads ever started reading and writingon it at the same time. Thread-local storage fixes this by making such resources onlyvisible in the thread where they’re being used.In this recipe, the use of threading.local() makes the LazyConnection class supportone connection per thread, as opposed to one connection for the entire process. It’s asubtle but interesting distinction.Under the covers, an instance of threading.local() maintains a separate instancedictionary for each thread. All of the usual instance operations of getting, setting, anddeleting values just manipulate the per-thread dictionary. The fact that each thread usesa separate dictionary is what provides the isolation of data.