Search
☑️

250311_2134_threading과 asyncio의 차이점 및 동시 요청 처리 방식

threadingasyncio의 차이점 및 동시 요청 처리 방식

threading

OS 레벨의 스레드를 활용하여 동시 실행을 처리
Python의 **Global Interpreter Lock(GIL)**로 인해 CPU 바운드 작업에서는 성능이 크게 향상되지 않음
I/O 바운드 작업(네트워크 요청, 파일 읽기/쓰기 등)에 적합
컨텍스트 스위칭이 필요하므로 성능 오버헤드가 있음
요청이 동시에 들어오면 각 요청마다 새로운 스레드를 생성할 수 있어 중복 처리가 발생할 가능성 있음
threading 사용 시, QueueLock을 이용해 중복 요청을 방지해야 함

asyncio

싱글 스레드, 싱글 프로세스 기반의 코루틴(async/await) 방식
GIL의 영향을 덜 받으며, 비동기 I/O 작업을 효율적으로 처리
이벤트 루프(Event Loop)를 이용하여 태스크(task)를 관리
asyncio.get_event_loop()로 실행 중인 이벤트 루프를 가져와 동일한 루프 내에서 작업을 처리할 수 있음 → 중복 요청 방지 가능
asyncio.ensure_future() 또는 asyncio.create_task()를 활용하면 동일한 이벤트 루프에서 여러 개의 비동기 작업을 처리 가능

threading에서 2번 요청받아 처리되는 문제 원인

동시 요청 처리 방식이 서로 독립적인 스레드에서 실행되기 때문
요청을 받을 때마다 새로운 스레드를 생성하고, 각 스레드가 동일한 요청을 처리하면서 중복 실행 발생 가능
예를 들어, Flask + threading 조합을 사용할 경우 동일한 API 요청이 거의 동시에 들어오면 두 개의 스레드에서 같은 로직을 실행하는 경우가 있음

asyncio.get_event_loop()를 활용한 해결 방식

asyncio.get_event_loop()를 이용하면 하나의 이벤트 루프에서 작업을 관리할 수 있음
동일한 루프 내에서 실행되므로, 특정 요청을 여러 번 실행하는 경우를 방지 가능
asyncio.Lock 또는 asyncio.Queue를 사용하여 중복 요청 방지 가능

threading vs asyncio 비교 정리

항목
threading
asyncio
실행 방식
멀티스레드
싱글 스레드 + 이벤트 루프
GIL 영향
있음
없음 (I/O 바운드 작업 시)
주 사용 사례
CPU 바운드, 동시 요청 처리
I/O 바운드, 네트워크 요청 최적화
중복 요청 가능성
있음 (스레드가 여러 번 실행될 수 있음)
적음 (이벤트 루프에서 관리)
컨텍스트 스위칭 비용
높음
낮음

예제 코드 비교

threading 예제 (중복 요청 가능성 있음)

import threading import time def process_request(request_id): print(f"Processing request {request_id}") time.sleep(2) # Simulate work print(f"Completed request {request_id}") # 두 개의 스레드가 동일한 요청을 처리하는 상황 발생 가능 thread1 = threading.Thread(target=process_request, args=(1,)) thread2 = threading.Thread(target=process_request, args=(1,)) thread1.start() thread2.start() thread1.join() thread2.join()
Python
복사
출력 예시:
Processing request 1 Processing request 1 Completed request 1 Completed request 1
Plain Text
복사
동일한 요청 1이 두 번 처리됨

asyncio 예제 (이벤트 루프 활용)

import asyncio async def process_request(request_id): print(f"Processing request {request_id}") await asyncio.sleep(2) # 비동기 대기 (블로킹 없음) print(f"Completed request {request_id}") async def main(): loop = asyncio.get_event_loop() task1 = loop.create_task(process_request(1)) task2 = loop.create_task(process_request(2)) await asyncio.gather(task1, task2) asyncio.run(main())
Python
복사
출력 예시:
Processing request 1 Processing request 2 Completed request 1 Completed request 2
Plain Text
복사
요청이 개별적으로 실행되지만 이벤트 루프 내에서 관리되므로 중복 실행 방지 가능

결론

threading은 OS 레벨에서 동작하며, 동일한 요청이 중복 실행될 가능성이 있음
asyncio는 이벤트 루프 기반이므로, 동일한 요청을 관리하는 방식이 다르고 중복 요청을 줄일 수 있음
asyncio.get_event_loop()를 활용하면, 기존의 이벤트 루프에서 요청을 처리하여 중복 실행을 막을 수 있음
I/O 바운드 작업(네트워크, 파일 처리 등)에서는 asyncio를 적극 활용하는 것이 성능적으로 유리함

안녕하세요

관련 기술 문의와 R&D 공동 연구 사업 관련 문의는 “glory@keti.re.kr”로 연락 부탁드립니다.

Hello

For technical and business inquiries, please contact me at “glory@keti.re.kr”