파이썬 Dependency Injector Thread 다루기 (feat. Consumer)

ILOV-IT·2023년 10월 30일

의존성 주입은 5대 객체지향 개념 중 직관성이 가장 떨어진다. 시간의 흐름과 동떨어져 보이기 때문이다. 하지만 의존성 주입을 사용하면 코드의 재사용이 쉬워진다. 코드를 크게 고치지 않고 주입하면 기계 장치의 모듈처럼 크게 손대지 않고 프로그램 중간에 끼울 수 있다. 파이썬에는 의존성 주입을 돕는 라이브러리가 존재하는데, Dependency Injector가 대표적이다.

이번 기록에서는 Dependency Injector를 통해 Consumer Thread를 구성해 보았다. Container 클래스를 살펴보면, 1)큐를 싱글톤으로 생성하고, 2)Thread가 실행하는 메소드와 큐를 엮고, 3)Thread의 객체로서 2번을 주입한다. Dependency Injector 웹사이트에 별다른 설명이 없고, 구글에도 예시 코드가 찾을 수 없어, 해당 웹사이트의 다른 코드를 참조해 구현해 보았다.

import threading
import queue
import time
from dependency_injector import containers, providers

def consumer(queue_object):

    while True:
        print("Waiting...")
        print(f"I woke up by '{queue_object.get()}'")
        time.sleep(2) # 쓰레드가 느리게 처리하는걸 표현


class Container(containers.DeclarativeContainer):

    queue_provider = providers.ThreadSafeSingleton(queue.Queue)

    consumer = providers.Callable(
        consumer,
        queue_object=queue_provider,
    )

    thread_factory = providers.Factory(
        threading.Thread,
        target=consumer.provider,
    )


if __name__ == "__main__":

    container = Container()

    thread = container.thread_factory(name="Thread")
    thread.start()

    time.sleep(5)
    
    container.queue_provider().put("일어나라1")
    container.queue_provider().put("일어나라2")
    container.queue_provider().put("일어나라3")
    container.queue_provider().put("일어나라4")
    container.queue_provider().put("일어나라5")


비교를 위해 다음은 의존성 주입을 사용하지 않은 Consumer Thread 다. 의존성 주입을 사용하지 않은 Consumer, Producer 코드는 여러 블로그에 설명이 있으므로 추가적인 설명을 생략한다.

from threading import *
import time
import queue


def consumer(q):
	while True:
    	print("waiting")
        print("woke up by ", q.get())


if __name__ == "__main__":

	q = queue.Queue()
    thread = Thread(target=consumer, arg=(q,))
    thread.start()
    
    q.put(1)
    time.sleep(5)
    q.put(5)


다음 코드는, API 서버에 새로운 값이 들어오면 그 값을 Queue에 넣고, Thread가 차례대로 큐를 처리하도록 하는 코드다. 큐를 사용하는 이유는 Thread의 처리 속도가 느리고, 하나씩 순차적으로 처리해야 하는 상황이기 때문이다. (Thread에 먼저 들어온 요청이 처리되지 않은 채, 다음 요청이 들어오면 그 요청을 씹어버릴 수 있는 문제 발생).

◇ 의존성 주입된 컨테이너에서 큐를 꺼내고, 큐에 쓰레드가 해야할 값을 넣어줌. 그러면 쓰레드는 queue.get으로 값을 꺼내서 처리함.

@inject
def sub_routine(
	message_queue: queue.Queue = Depends(Provide[Container.queue_provider])
):

    message_queue.put("쓰레드야! 일어나서 일해!")


| 참 고 |
https://python-dependency-injector.ets-labs.org/providers/singleton.html

profile
because we know you'll love it

0개의 댓글