线程局部变量--threading.local
一、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)