当我们使用线程时,如果多线程同时对一个全局变量进行修改,很容易造成错误,这是我们可以利用加锁的方式,让线程一个一个进行操作
同样我们可以threading.local来实现
threading.local的使用
不使用threading.local时
from threading import Thread from threading import local import time # xianglong = local() xianglong = -1 def task(arg): global xianglong xianglong = arg time.sleep(2) print(xianglong) for i in range(10): t = Thread(target=task,args=(i,)) t.start()
输出结果
9 9 9 9 9 9 9 9 9 9
可以看到,由于所以的线程都在修改同一个全局变量,导致最后无法得到想要的结果
使用threading.loacl
from threading import Thread from threading import local import time from threading import get_ident # 特殊的对象 xianglong = local() def task(arg): # 对象.val = 1/2/3/4/5 xianglong.value = arg time.sleep(2) print(xianglong.value) for i in range(10): t = Thread(target=task,args=(i,)) t.start()
输出结果
0 1 2 3 5 4 6 7 8 9
使用了threading.local会为每一个线程创建一个独立的空间存储变量
get_ident
我们可以通过get_ident来获取每一个线程的独特的值,类似于线程号
from threading import Thread from threading import get_ident def task(arg): print(get_ident()) for i in range(10): t = Thread(target=task,args=(i,)) t.start()
输出结果
5028 6088 7340 8384 8648 3372 8752 5620 6308 4396
自己写一个类似于threading的功能
try: from greenlet import getcurrent as get_ident except Exception as e: from threading import get_ident from threading import Thread import time class Local(object): def __init__(self): object.__setattr__(self,'storage',{}) def __setattr__(self, k, v): ident = get_ident() if ident in self.storage: self.storage[ident][k] = v else: self.storage[ident] = {k: v} def __getattr__(self, k): ident = get_ident() return self.storage[ident][k] obj = Local() def task(arg): obj.val = arg obj.xxx = arg print(obj.val) for i in range(10): t = Thread(target=task,args=(i,)) t.start()
用.给对象设置值时会调用__setattr__方法,这里我们导入时使用了try捕捉异常,先导入协程的get_ident,如果没有则导入线程的,所以我们的方法比threading.loacl多了一个实现协程的功能,不同的线程可以在obj对象的storage字典中利用自己的get_ident值为键生成对
应的值,在定义self.storage时我们调用的是父类object类的__setattr__方法,那是因为我们自己的类定义了这个方法,直接使用self.storage={}会执行自己类的__setattr__方法,会出问题