Search
moon
sun
☑️

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

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

threading

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

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”