Skip to content

Commit df22dfb

Browse files
author
yidao620c
committed
12.6小节完成
1 parent f59a275 commit df22dfb

File tree

1 file changed

+75
-75
lines changed

1 file changed

+75
-75
lines changed

source/c12/p06_storing_thread_specific_state.rst

+75-75
Original file line numberDiff line numberDiff line change
@@ -5,90 +5,90 @@
55
----------
66
问题
77
----------
8-
You need to store state that’s specific to the currently executing thread and not visible
9-
to other threads.
8+
你需要保存正在运行线程的状态,这个状态对于其他的线程是不可见的。
109

1110
|
1211
1312
----------
1413
解决方案
1514
----------
16-
Sometimes in multithreaded programs, you need to store data that is only specific to
17-
the currently executing thread. To do this, create a thread-local storage object using
18-
threading.local(). Attributes stored and read on this object are only visible to the
19-
executing thread and no others.
20-
As an interesting practical example of using thread-local storage, consider the LazyCon
21-
nection context-manager class that was first defined in Recipe 8.3. Here is a slightly
22-
modified version that safely works with multiple threads:
23-
24-
from socket import socket, AF_INET, SOCK_STREAM
25-
import threading
26-
27-
class LazyConnection:
28-
def __init__(self, address, family=AF_INET, type=SOCK_STREAM):
29-
self.address = address
30-
self.family = AF_INET
31-
self.type = SOCK_STREAM
32-
self.local = threading.local()
33-
34-
def __enter__(self):
35-
if hasattr(self.local, 'sock'):
36-
raise RuntimeError('Already connected')
37-
self.local.sock = socket(self.family, self.type)
38-
self.local.sock.connect(self.address)
39-
return self.local.sock
40-
41-
def __exit__(self, exc_ty, exc_val, tb):
42-
self.local.sock.close()
43-
del self.local.sock
44-
45-
In this code, carefully observe the use of the self.local attribute. It is initialized as an
46-
instance of threading.local(). The other methods then manipulate a socket that’s
47-
stored as self.local.sock. This is enough to make it possible to safely use an instance
48-
of LazyConnection in multiple threads. For example:
49-
50-
from functools import partial
51-
def test(conn):
52-
with conn as s:
53-
s.send(b'GET /index.html HTTP/1.0\r\n')
54-
s.send(b'Host: www.python.org\r\n')
55-
56-
s.send(b'\r\n')
57-
resp = b''.join(iter(partial(s.recv, 8192), b''))
58-
59-
print('Got {} bytes'.format(len(resp)))
60-
61-
if __name__ == '__main__':
62-
conn = LazyConnection(('www.python.org', 80))
63-
64-
t1 = threading.Thread(target=test, args=(conn,))
65-
t2 = threading.Thread(target=test, args=(conn,))
66-
t1.start()
67-
t2.start()
68-
t1.join()
69-
t2.join()
70-
71-
The reason it works is that each thread actually creates its own dedicated socket con‐
72-
nection (stored as self.local.sock). Thus, when the different threads perform socket
73-
operations, they don’t interfere with one another as they are being performed on dif‐
74-
ferent sockets.
15+
有时在多线程编程中,你需要只保存当前运行线程的状态。
16+
要这么做,可使用 ``thread.local()`` 创建一个本地线程存储对象。
17+
对这个对象的属性的保存和读取操作都只会对执行线程可见,而其他线程并不可见。
18+
19+
作为使用本地存储的一个有趣的实际例子,
20+
考虑在8.3小节定义过的 ``LazyConnection`` 上下文管理器类。
21+
下面我们对它进行一些小的修改使得它可以适用于多线程:
22+
23+
.. code-block:: python
24+
25+
from socket import socket, AF_INET, SOCK_STREAM
26+
import threading
27+
28+
class LazyConnection:
29+
def __init__(self, address, family=AF_INET, type=SOCK_STREAM):
30+
self.address = address
31+
self.family = AF_INET
32+
self.type = SOCK_STREAM
33+
self.local = threading.local()
34+
35+
def __enter__(self):
36+
if hasattr(self.local, 'sock'):
37+
raise RuntimeError('Already connected')
38+
self.local.sock = socket(self.family, self.type)
39+
self.local.sock.connect(self.address)
40+
return self.local.sock
41+
42+
def __exit__(self, exc_ty, exc_val, tb):
43+
self.local.sock.close()
44+
del self.local.sock
45+
46+
代码中,自己观察对于 ``self.local`` 属性的使用。
47+
它被初始化尾一个 ``threading.local()`` 实例。
48+
其他方法操作被存储为 ``self.local.sock`` 的套接字对象。
49+
有了这些就可以在多线程中安全的使用 ``LazyConnection`` 实例了。例如:
50+
51+
::
52+
53+
from functools import partial
54+
def test(conn):
55+
with conn as s:
56+
s.send(b'GET /index.html HTTP/1.0\r\n')
57+
s.send(b'Host: www.python.org\r\n')
58+
59+
s.send(b'\r\n')
60+
resp = b''.join(iter(partial(s.recv, 8192), b''))
61+
62+
print('Got {} bytes'.format(len(resp)))
63+
64+
if __name__ == '__main__':
65+
conn = LazyConnection(('www.python.org', 80))
66+
67+
t1 = threading.Thread(target=test, args=(conn,))
68+
t2 = threading.Thread(target=test, args=(conn,))
69+
t1.start()
70+
t2.start()
71+
t1.join()
72+
t2.join()
73+
74+
它之所以行得通的原因是每个线程会创建一个自己专属的套接字连接(存储为self.local.sock)。
75+
因此,当不同的线程执行套接字操作时,由于操作的是不同的套接字,因此它们不会相互影响。
7576

7677
|
7778
7879
----------
7980
讨论
8081
----------
81-
Creating and manipulating thread-specific state is not a problem that often arises in
82-
most programs. However, when it does, it commonly involves situations where an object
83-
being used by multiple threads needs to manipulate some kind of dedicated system
84-
resource, such as a socket or file. You can’t just have a single socket object shared by
85-
everyone because chaos would ensue if multiple threads ever started reading and writing
86-
on it at the same time. Thread-local storage fixes this by making such resources only
87-
visible in the thread where they’re being used.
88-
In this recipe, the use of threading.local() makes the LazyConnection class support
89-
one connection per thread, as opposed to one connection for the entire process. It’s a
90-
subtle but interesting distinction.
91-
Under the covers, an instance of threading.local() maintains a separate instance
92-
dictionary for each thread. All of the usual instance operations of getting, setting, and
93-
deleting values just manipulate the per-thread dictionary. The fact that each thread uses
94-
a separate dictionary is what provides the isolation of data.
82+
在大部分程序中创建和操作线程特定状态并不会有什么问题。
83+
不过,当出了问题的时候,通常是因为某个对象被多个线程使用到,用来操作一些专用的系统资源,
84+
比如一个套接字或文件。你不能让所有线程贡献一个单独对象,
85+
因为多个线程同时读和写的时候会产生混乱。
86+
本地线程存储通过让这些资源只能在被使用的线程中可见来解决这个问题。
87+
88+
本节中,使用 ``thread.local()`` 可以让 ``LazyConnection`` 类支持一个线程一个连接,
89+
而不是对于所有的进程都只有一个连接。
90+
91+
其原理是,每个 ``threading.local()`` 实例为每个线程维护着一个单独的实例字典。
92+
所有普通实例操作比如获取、修改和删除值仅仅操作这个字典。
93+
每个线程使用一个独立的字典就可以保证数据的隔离了。
94+

0 commit comments

Comments
 (0)