12.6 ä¿�å˜çº¿ç¨‹çš„状æ€�ä¿¡æ�¯Â¶
问题¶
ä½ éœ€è¦�ä¿�å˜æ£åœ¨è¿�行线程的状æ€�,这个状æ€�对于其他的线程是ä¸�å�¯è§�的。
解决方案¶
有时在多线程编程ä¸ï¼Œä½ 需è¦�å�ªä¿�å˜å½“å‰�è¿�行线程的状æ€�。
è¦�这么å�šï¼Œå�¯ä½¿ç”¨ thread.local() 创建一个本地线程å˜å‚¨å¯¹è±¡ã€‚
对这个对象的属性的ä¿�å˜å’Œè¯»å�–æ“�作都å�ªä¼šå¯¹æ‰§è¡Œçº¿ç¨‹å�¯è§�,而其他线程并ä¸�å�¯è§�。
作为使用本地å˜å‚¨çš„一个有趣的实际例å�,
考虑在8.3�节定义过的 LazyConnection 上下文管�器类。
下�我们对它进行一些�的修改使得它�以适用于多线程:
from socket import socket, AF_INET, SOCK_STREAM
import threading
class LazyConnection:
def __init__(self, address, family=AF_INET, type=SOCK_STREAM):
self.address = address
self.family = AF_INET
self.type = SOCK_STREAM
self.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
代ç �ä¸ï¼Œè‡ªå·±è§‚察对于 self.local 属性的使用。
它被�始化为一个 threading.local() 实例。
其他方法æ“�作被å˜å‚¨ä¸º self.local.sock 的套接å—对象。
有了这些就å�¯ä»¥åœ¨å¤šçº¿ç¨‹ä¸å®‰å…¨çš„使用 LazyConnection 实例了。例如:
from functools import partial
def test(conn):
with conn as s:
s.send(b'GET /index.html HTTP/1.0\r\n')
s.send(b'Host: www.python.org\r\n')
s.send(b'\r\n')
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()
å®ƒä¹‹æ‰€ä»¥è¡Œå¾—é€šçš„åŽŸå› æ˜¯æ¯�个线程会创建一个自己专属的套接å—连接(å˜å‚¨ä¸ºself.local.sock)。 å› æ¤ï¼Œå½“ä¸�å�Œçš„çº¿ç¨‹æ‰§è¡Œå¥—æŽ¥å—æ“�作时,由于æ“�作的是ä¸�å�Œçš„套接å—ï¼Œå› æ¤å®ƒä»¬ä¸�会相互影å“�。
讨论¶
在大部分程åº�ä¸åˆ›å»ºå’Œæ“�作线程特定状æ€�å¹¶ä¸�会有什么问题。 ä¸�è¿‡ï¼Œå½“å‡ºäº†é—®é¢˜çš„æ—¶å€™ï¼Œé€šå¸¸æ˜¯å› ä¸ºæŸ�个对象被多个线程使用到,用æ�¥æ“�作一些专用的系统资æº�, æ¯”å¦‚ä¸€ä¸ªå¥—æŽ¥å—æˆ–æ–‡ä»¶ã€‚ä½ ä¸�能让所有线程共享一个å�•独对象, å› ä¸ºå¤šä¸ªçº¿ç¨‹å�Œæ—¶è¯»å’Œå†™çš„æ—¶å€™ä¼šäº§ç”Ÿæ··ä¹±ã€‚ 本地线程å˜å‚¨é€šè¿‡è®©è¿™äº›èµ„æº�å�ªèƒ½åœ¨è¢«ä½¿ç”¨çš„线程ä¸å�¯è§�æ�¥è§£å†³è¿™ä¸ªé—®é¢˜ã€‚
本节ä¸ï¼Œä½¿ç”¨ thread.local() å�¯ä»¥è®© LazyConnection 类支æŒ�一个线程一个连接,
而�是对于所有的进程都�有一个连接。
其原ç�†æ˜¯ï¼Œæ¯�个 threading.local() 实例为æ¯�个线程维护ç�€ä¸€ä¸ªå�•独的实例å—典。
所有普通实例æ“�作比如获å�–ã€�ä¿®æ”¹å’Œåˆ é™¤å€¼ä»…ä»…æ“�作这个å—典。
æ¯�个线程使用一个独立的å—典就å�¯ä»¥ä¿�è¯�æ•°æ�®çš„隔离了。