오히려 좋아..

상황이 나쁘게만 흘러가는 것 같을 때 외쳐보자.. .

궁금한 마음으로 포트폴리오 보기

Language/Python

[Python] Thread-safe 한 자료구조

junha6316 2021. 4. 29. 19:56

 

멀티스레드 환경에서 스레드간 통신을 위해 자료구조를 사용한다고 한다. 하지만 동시에 여러 스레드에서 하나의 변수에 접근시 임계영역 문제가 발생하게 된다. 그렇기 때문에 자료구조 역시 Thread-safe 하게 만들 필요가 있다.

아래는 Thread-safe 구현한 set 클래스 이다.(파이썬 동시성 프로그래밍, 에이콘 출판사 에서 발췌)

from threading import Lock

class LockedSet(set):

    def __init__(self, *args, **kwargs):
        self._lock= Lock()
        super(LockedSet, self).__init__(*args, **kwargs)
        
    def add(self, elem):
        with self._lock:
        #with keyword는 자원을 얻고 반납하는 경우 사용하는 키워드
            super(LockedSet, self).add(elem)
            
  	def remove(self, elem):
        with self._lock:
            super(LockedSet, self).remove(elem)
            
    def __contains__(self, elem):
        with self._lock:
            super(LockedSet, self).__contains__(elem)

 

재사용성을 높이면서 하는 방법은 함수 데코레이터를 사용하는 방법이있다.


def lock(method):
    def newmethod(self, *args, **kwargs):
    	with self._lock:
        	return method(self, *args, **kwargs)
            
    return newmethod
    
class DecoratorLockedSet(set):

    def __init__(self, *args, **kwargs):
        self._lock = Lock()
        super(DecoratorLockedSet, self).__init__(*args, **kwargs)
        
    @locked_method
    def add(self, *args, **kwargs):
        return super(DecoratorLockedSet, self).add(elem)
       
    @locked_method
    def remove(self, *args, **kwargs):
        return super(DecoratorLockedSet, self).remove(elem) 

 

이것보다 더 재사용성을 높이는 방법으로는 클래스 데코레이터를 사용하면 된다.

from threading import Lock


def lock_class(methodNames, lockfactory):
    return lambda cls: make_threadsafe(cls, methodNames, lockfactory)
    
def locked_method(method):
    with self._lock:
        return method(self, *args, **kwargs)
    
    locked_method.__name__ = f"{locked_method}{method.__name__}"
    locked_method.__is_locked =True
    return locked_method 
    
def make_threadsafe(cls, methodnames, lockfactory):
    init = cls.__init__
    def newinit(self, *args, **kwargs):
        init(self, *args, **kwargs)
        self._lock = lockfactory()
    cls.__init__ = newinit
    for methodName in methodNames:
        oldmethod = getattr(cls, methodName)
        newmethod = lock_method(oldmethod)
        setattr(cls, methodname, newmethod)
    return cls
    
@lock_class(['add', 'remove'], Lock)
class ClassDecoratorLockedSet(set):
    @lock_method
    def lockedMethod(self):
        print("this section of our code would be thread safe")