自動化テストプラットフォーム化[v 1.0.0][イベントリソースロック]


リソース設計では、すべてのリソースがプロファイルから読み出されてインスタンスとして保存されるため、通常、リソースの名前は一意であるため、リソース名をロックのキーワードとし、その後、対応するリソースのロック情報を1つの構造体で保存し、信号量を用いてロック状態を識別することができる
スレッド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