一、threading.local的功能了解
通过下面两个例子可以直观的了解到local的作用。
1.普通线程操作同一变量:
import time import threading class Foo(object): def __init__(self): self.num = 0 val2 = Foo() def task(i): val2.num = i time.sleep(1) print(val2.num) for i in range(4): t = threading.Thread(target=task,args=(i,)) t.start() """ 3 3 3 3 """
2.local操作同一变量:
import time import threading # 当每个线程在执行 val1.xx=1 ,在内部会为此线程开辟一个空间,来存储 xx=1 # val1.xx,找到此线程自己的内存地址去取自己存储 xx val1 = threading.local() def task(i): val1.num = i time.sleep(1) print(val1.num) for i in range(4): t = threading.Thread(target=task,args=(i,)) t.start() """ 0 1 2 3 """
local可以返回一个线程局部变量,通过使用线程局部变量可以很简捷地隔离多线程访问的竞争资源,从而简化多线程井发访问的编程处理。
线程局部变量(Thread Local Variable)的功用其实非常简单,就是为每一个使用该变量的线程都提供一个变量的副本,使每一个线程都可以独立地改变自己的副本,而不会和其他线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有该变量一样。
线程局部变量和其他同步机制一样,都是为了解决多线程中对共享资源的访问冲突的。在普通的同步机制中,是通过为对象加锁来实现多个线程对共享资源的安全访问的。由于共享资源是多个线程共享的,所以要使用这种同步机制,就需要很细致地分析什么时候对共享资源进行读写,什么时候需要锁定该资源,什么时候释放对该资源的锁定等。在这种情况下,系统并没有将这份资源复制多份,只是采用安全机制来控制对这份资源的的访问而已。
线程局部变量从另一个角度来解决多线程的并发访问问题。线程局部变量将需要并发访问的资源复制多份,每个线程都拥有自己的资源副本,从而也就没有必要对该资源进行同步了。线程局部变量提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的整个变量放到线程局部变量中,或者把该对象中与线程相关的状态放入线程局部变量中保存。
线程局部变量并不能替代同步机制,两者面向的问题领域不同。同步机制是为了同步多个线程对共享资源的并发访问,是多个线程之间进行通信的有效方式;而线程局部变量是为了隔离多个线程的数据共享,从根本上避免多个钱程之间对共享资源(变量)的竞争,也就不需要对多个线程进行同步了。
通常建议,如果多个线程之间需要共享资源,以实现线程通信,则使用同步机制;如果仅仅需要隔离多个线程之间的共享冲突,则可以使用线程局部变量。
二、线程的唯一标识
import threading from threading import get_ident def task(): ident = get_ident() print(ident) for i in range(20): t = threading.Thread(target=task) t.start()
三、自定义threading.local
import threading """ storage = { 1111:{'x1':[0,1,2,3]}, 1112:{'x1':1} 1113:{'x1':2} 1114:{'x1':3} 1115:{'x1':4} } """ class Local(object): def __init__(self): object.__setattr__(self,'storage',{}) def __setattr__(self, key, value): ident = threading.get_ident() if ident in self.storage: self.storage[ident][key] = value else: self.storage[ident] = {key:value} def __getattr__(self, item): ident = threading.get_ident() if ident not in self.storage: return return self.storage[ident].get(item) local = Local() def task(arg): local.x1 = arg print(local.x1) for i in range(5): t = threading.Thread(target=task,args=(i,)) t.start()
四、加强版threading.local
import threading """ storage = { 1111:{'x1':[]}, 1112:{'x1':[]} 1113:{'x1':[]} 1114:{'x1':[]} 1115:{'x1':[]}, 1116:{'x1':[]} } """ class Local(object): def __init__(self): object.__setattr__(self,'storage',{}) def __setattr__(self, key, value): ident = threading.get_ident() if ident in self.storage: self.storage[ident][key].append(value) else: self.storage[ident] = {key:[value,]} def __getattr__(self, item): ident = threading.get_ident() if ident not in self.storage: return return self.storage[ident][item][-1] local = Local() def task(arg): local.x1 = arg print(local.x1) for i in range(5): t = threading.Thread(target=task,args=(i,)) t.start()
flask源码关于local的实现(Local具体分析见下一章)
try: # 协程 from greenlet import getcurrent as get_ident except ImportError: try: from thread import get_ident except ImportError: from _thread import get_ident """ __storage__ = { 1111:{"stack":[xx] } } """ class Local(object): def __init__(self): # self.__storage__ = {} # self.__ident_func__ = get_ident object.__setattr__(self, "__storage__", {}) object.__setattr__(self, "__ident_func__", get_ident) def __iter__(self): return iter(self.__storage__.items()) def __release_local__(self): self.__storage__.pop(self.__ident_func__(), None) def __getattr__(self, name): try: return self.__storage__[self.__ident_func__()][name] except KeyError: raise AttributeError(name) def __setattr__(self, name, value): ident = self.__ident_func__() # 1111 storage = self.__storage__ try: storage[ident][name] = value except KeyError: storage[ident] = {name: value} def __delattr__(self, name): try: del self.__storage__[self.__ident_func__()][name] except KeyError: raise AttributeError(name)