Python在内置的threading
模块中提供了Lock
类,该类相当于互斥锁
,可以保护各线程数据结构不被破坏。
案例:新建一个Counter
类,统计传感器采样获得的样本数量。
### 计数器
class Counter(object):
def __init__(self):
self.count = 0
def increment(self,offset):
self.count += offset
### 传感器
def woker(sensor_index,how_many,counter):
for _ in range(how_many):
## Read from the sensor
##....
counter.increment(1)
#### run_threads函数
def run_threads(func,how_many,counter):
threads = []
for i in range(5):
args = (i,how_many,counter)
thread = Thread(target=func,args = args)
threads.append(thread)
thread.start()
for thread in threads:
thread.joint()
if __name__=='__main__':
###执行
how_many = 10**5
counter = Counter()
run_threads(worker,how_many,counter)
print(5*how_many,counter.count)
输出结果:
500000 278328
可以看出,两者数据相差较大,这是由于程序在执行self.count += offset
语句时,并非原子操作,而是可以分解为:
value = getattr(counter,'count')
result = value + offset
setattr(counter,'count',result)
上述三个操作,在任意两个操作之间都可能发生线程切换,这种频繁的线程切换,交错执行可能会令线程把旧的value
设置给Counter
。
因此,在不加锁的前提下,允许多条线程修改同一对象,那么程序的数据结构可能会遭到破坏。
我们可以用互斥锁
保护Counter
对象,使得多个线程同时访问value
值的时候,不会将该值破坏。同一时刻,只有一个线程能够获得这把锁。如下:
class LockingCounter(object):
def __init__(self):
self.lock = Lock()
self.count = 0
def increment(self,offset):
with self.lock:###判断锁状态
self.count += offset
if __name__ == '__main__':
counter = LockingCounter()
run_threads(worker,how_many,counter)
print(5*how_many,counter.count)