当我们使用线程时,如果多线程同时对一个全局变量进行修改,很容易造成错误,这是我们可以利用加锁的方式,让线程一个一个进行操作
同样我们可以threading.local来实现
threading.local的使用
不使用threading.local时
from threading import Threadfrom threading import localimport time# xianglong = local()xianglong = -1def task(arg): global xianglong xianglong = arg time.sleep(2) print(xianglong)for i in range(10): t = Thread(target=task,args=(i,)) t.start()
输出结果
9999999999
可以看到,由于所以的线程都在修改同一个全局变量,导致最后无法得到想要的结果
使用threading.loacl
from threading import Threadfrom threading import localimport timefrom 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()
输出结果
0123546789
使用了threading.local会为每一个线程创建一个独立的空间存储变量
get_ident
我们可以通过get_ident来获取每一个线程的独特的值,类似于线程号
from threading import Threadfrom threading import get_identdef task(arg): print(get_ident())for i in range(10): t = Thread(target=task,args=(i,)) t.start()
输出结果
5028608873408384864833728752562063084396
自己写一个类似于threading的功能
try: from greenlet import getcurrent as get_identexcept Exception as e: from threading import get_identfrom threading import Threadimport timeclass 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__方法,会出问题