在资源设计中,所有的资源从配置文件中读出后成为实例保存,通常情况下资源的名字是唯一的,因此可以通过资源名作为的锁的关键字,然后使用一个结构体来保存相应资源的锁信息,并使用信号量来标识锁状态
假设线程A对该资源进行操作,重置了信号量,当线程B也需要使用该资源并对其进行加锁的时候,就会因为该资源所对应的信号量而阻塞住线程,直到该信号量被线程A置位,代表线程A对该资源的占用结束,线程B才能继续
from core.result.logger import logger
from core.utilities.time import get_local_time
from threading import Event, Thread
import time
class ResourceIsLocked(Exception):
def __init__(self, resource, event, timeout):
super().__init__(f"Resource {resource} is locked by {event} and not released in {timeout}s")
class InvalidLockOperation(Exception):
pass
class ResourceLockPool:
"""
资源锁池
"""
def __init__(self, log=None):
self.log = log if log is not None else logger.register("ResourceLockPool", default_level="INFO")
self.resource = dict()
def lock(self, resource, event, timeout=60):
"""
锁定资源
"""
if resource.name in self.resource:
lock = self.resource[resource.name]['lock']
event_name = self.resource[resource.name]['event']
if not lock.wait(timeout):
raise ResourceIsLocked(resource.name, event_name, timeout)
self.log.info(f"Lock {resource.name}: time: {get_local_time()}")
self.resource[resource.name] = {
"event": event,
"date": get_local_time(),
"lock": Event()
}
def release(self, resource, event):
"""
释放资源
"""
if resource.name in self.resource:
if self.resource[resource.name]['event'] == event:
self.log.info(f"Release lock for {resource.name}")
self.resource[resource.name]['lock'].set()
self.resource.pop(resource.name)
else:
raise InvalidLockOperation(
f"{resource.name} is locked by {self.resource[resource.name]['event']}")
else:
raise InvalidLockOperation(f'{resource.name} is not locked')
if __name__ == "__main__":
class TestResource:
"""
代表测试资源类
"""
def __init__(self, name):
self.name = name
log = logger.register("testlog")
pool = ResourceLockPool(log)
device1 = TestResource('device1')
"""
测试:定义两个方法,用来在两个线程中对同一个资源进行访问
"""
def test_method1():
pool.lock(device1, "event1")
log.info("method1 start")
time.sleep(10)
pool.release(device1, "event1")
log.info("method1 stop")
def test_method2():
pool.lock(device1, "event2")
log.info("method2 start")
time.sleep(10)
pool.release(device1, "event2")
log.info("method2 stop")
thread1 = Thread(target=test_method1)
thread2 = Thread(target=test_method2)
threads = [thread1, thread2]
for t in threads:
t.start()
for t in threads:
t.join()
- ResourceLockPool中的resource字典类型的属性用于存放资源锁,资源锁的值被定义为一个字典类型的结构体,包含3个key,event表示占用该资源的事件名称,date表示锁产生的时间,lock用来存放信号量,为了和event事件区分开,使用lock来表示
- lock方法用于加锁,该方法首先判断资源是否在resource属性中,如果存在则表示该资源已经被上锁,这时就需要调用该资源对应的信号量的wait方法,等待其他线程设置该信号
- 引入超时时间防止死锁,超过设定时间后该信号量得不到置位,则wait方法返回False,抛出异常,告诉lock方法的调用者在超时时间内,资源锁无法释放
- release方法用于释放资源,如果资源不在resouce属性中,则抛出异常,告诉调用者资源没有被锁,否则就判断资源是否是调用者所在的事件锁定的,如果不是也抛出一个异常,表示该资源并非由其锁定,最后该资源所对应的信号量被置位,并且该资源从resource属性中被移除
[2020-08-01 11:20:18,258][testlog]-<thread:106940>-(line:33), [INFO]: Lock device1: time: August 01, 20 11:20:18
[2020-08-01 11:20:18,258][testlog]-<thread:106940>-(line:70), [INFO]: method1 start
[2020-08-01 11:20:28,271][testlog]-<thread:106940>-(line:46), [INFO]: Release lock for device1
[2020-08-01 11:20:28,271][testlog]-<thread:106940>-(line:73), [INFO]: method1 stop
[2020-08-01 11:20:28,271][testlog]-<thread:163360>-(line:33), [INFO]: Lock device1: time: August 01, 20 11:20:28
[2020-08-01 11:20:28,271][testlog]-<thread:163360>-(line:78), [INFO]: method2 start
[2020-08-01 11:20:38,280][testlog]-<thread:163360>-(line:46), [INFO]: Release lock for device1
[2020-08-01 11:20:38,280][testlog]-<thread:163360>-(line:81), [INFO]: method2 stop