十二、threading.local

1、线程之间资源共享和threading.local

代码:

 1 from threading import local, Thread
 2 
 3 
 4 i = None
 5 
 6 
 7 def func(num):
 8     global i
 9     i = num
10     print(i)
11 
12 
13 threads = []
14 for i in range(10):
15     threads.append(Thread(target=func, args=(i, )))
16 
17 for thread in threads:
18     thread.start()
19 
20 for thread in threads:
21     thread.join()

输出:

 由于没有阻塞操作,导致看不出没有给数据加锁会造成什么影响,

加入阻塞操作后的代码:

 1 from threading import local, Thread
 2 from time import sleep
 3 
 4 
 5 i = None
 6 
 7 
 8 def func(num):
 9     global i
10     i = num
11     sleep(2)
12     print(i)
13 
14 
15 threads = []
16 for i in range(10):
17     threads.append(Thread(target=func, args=(i, )))
18 
19 for thread in threads:
20     thread.start()
21 
22 for thread in threads:
23     thread.join()

输出:

 由于加入阻塞操作后,所有的线程在未输入i之前,争夺资源i,使得每个线程最终输出的i都是最后一次修改的数据,

那么有什么方法,可以使得每次在线程中访问同一个数据,但都是自己的资源,线程之间的数据相互隔离呢?

使用threading.local,

代码:

 1 from threading import local as Local, Thread, RLock
 2 from time import sleep
 3 
 4 
 5 i = None
 6 local = Local()
 7 
 8 
 9 def func(num):
10     local.x = num
11     sleep(0.3)
12     print(local.x)
13 
14 
15 threads = []
16 for i in range(10):
17     threads.append(Thread(target=func, args=(i, )))
18 
19 for thread in threads:
20     thread.start()
21 
22 for thread in threads:
23     thread.join()

输出:

 2、threading.local原理

  threading.local是一个类,当实例化后,使用点号给对象某个属性赋值时就会调用__setattr__方法,使用点号获取某个属性值时就会调用__getattr__,

我们可以在local对象中设置一个类似于字典的属性,每当要设置某个属性值或者访问某个属性时,就在__setattr__或__getattr__中找到对应线程存储的值(或者说为每个访问环境设置一个唯一标识符,对应访问环境中的值),

函数实现:

 1 from threading import Thread, get_ident
 2 from time import sleep
 3 
 4 storage = {}
 5 
 6 
 7 def set(k, v):
 8     # 获取当前线程或携程的唯一标识
 9     ident = get_ident()
10     if ident in storage:
11         storage[ident][k] = v
12     else:
13         storage[ident] = {k:v}
14 
15 def get(k):
16     ident = get_ident()
17     try:
18         res = storage[ident][k]
19     except Exception as e:
20         return None
21     else:
22         return res
23 
24 
25 def func(num):
26     set("x", num)
27     sleep(2)
28     print(get("x"))
29 
30 
31 threads = []
32 for i in range(10):
33     threads.append(Thread(target=func, args=(i, )))
34 
35 for thread in threads:
36     thread.start()
37 
38 for thread in threads:
39     thread.join()

输出:

 错误的类实现:

 1 from threading import Thread, get_ident
 2 from time import sleep
 3 
 4 
 5 class Local(object):
 6     def __init__(self):
 7         self.storage = {}
 8 
 9     def __setattr__(self, key, value):
10         ident = get_ident()
11         if ident in self.storage:
12             self.storage[ident][key] = value
13         else:
14             self.storage[ident] = {key: value}
15 
16     def __getattr__(self, item):
17         ident = get_ident()
18         try:
19             res = self.storage[ident][item]
20         except Exception as e:
21             return None
22         else:
23             return res
24 
25 local = Local()
26 
27 def func(num):
28     local.x = num
29     print(local.x)
30 
31 
32 threads = []
33 for i in range(10):
34     threads.append(Thread(target=func, args=(i, )))
35 
36 for thread in threads:
37     thread.start()
38 
39 for thread in threads:
40     thread.join()

报错信息:

 原因:

  不可以直接在初始化对象时,直接将对象设置storage为字典,因为当使用self.storage={}时,就会调用self.__setattr__("storage", {}),而第11行的storage属性还没有被设置,因而为NoneType类型

正确的类实现:

 1 from threading import Thread, get_ident
 2 from time import sleep
 3 
 4 
 5 class Local(object):
 6     def __init__(self):
 7         object.__setattr__(self, "storage", {})
 8 
 9     def __setattr__(self, key, value):
10         ident = get_ident()
11         if ident in self.storage:
12             self.storage[ident][key] = value
13         else:
14             self.storage[ident] = {key: value}
15 
16     def __getattr__(self, item):
17         ident = get_ident()
18         try:
19             res = self.storage[ident][item]
20         except Exception as e:
21             return None
22         else:
23             return res
24 
25 local = Local()
26 
27 def func(num):
28     local.x = num
29     print(local.x)
30 
31 
32 threads = []
33 for i in range(10):
34     threads.append(Thread(target=func, args=(i, )))
35 
36 for thread in threads:
37     thread.start()
38 
39 for thread in threads:
40     thread.join()

输出:

原文地址:https://www.cnblogs.com/loveprogramme/p/13430690.html