[Python] 동기, 비동기, 동시성, 병렬성

SIK407·2025년 12월 22일

Python & FastAPI

목록 보기
3/6
post-thumbnail

◽ 동기 vs 비동기

✔ 동기(Synchronous)

사전적으로 '동시에 일어난다'는 의미
프로그래밍에서 작업이 순차적으로 진행되는 것을 의미


위 이미지처럼 주문한 커피가 나올때까지, 뒤에 손님들은 기다려야 된다.

작업을 커피라고 생각을 해보자.
작업이 시작되면, 해당 작업이 완료될 때까지 다른 작업이 기다려야 된다.

task1 = threading
	.Thread(target=thread_work, args=("First thread", ))
task2 = threading
	.Thread(target=thread_work, args=("Second thread", ))


# 순차적인 두 개의 작업
task1.start() # 쓰레드 시작
task1.join()  # 쓰레드가 종료하면 반환

task2.start()
task2.join()

이처럼 task1을 마치고 쓰레드를 반환하면, task2가 실행이 된다.

우리가 평소에 평범하게 코드 작성하고 실행하면, 순차적으로 실행되는 형태가 동기(Synchronous) 방식이다.

하지만 여러 작업이 동시에 실행되어야 하는 경우,
각 작업의 완료를 기다리는 동안 시간이 소요되어 성능이 저하된다.

✔ 비동기(Asynchronous)

작업이 독립적으로 실행되며,
완료 여부를 기다리지 않고 다른 작업을 실행할 수 있는 방식을 의미


# 순차적인 두 개의 작업
task1.start() # 작업1 시작
task2.start() # 작업2 시작

task1.join()
task2.join()

이처럼 작업의 완료 여부와 상관없이 작업을 시작한다.

주로 I/O 작업 혹은 네트워크 요청과 같이 시간이 오래 걸리는 작업에 유용하다.

이러한 작업을 비동기적으로 처리하면,
프로그램은 작업이 완료되기를 기다리는 동안 다른 작업을 처리할 수 있으므로 전체적인 성능이 향상된다.

✔ 작업 소요 시간 차이

(작업이 20s, 7s, 10s, 8s로 순서대로 동시에 들어온다고 가정.)

첫번째 사진은 동기(Synchronous) 방식이다.
동기는 한 개의 작업을 끝낸 후, 다음 작업으로 넘어가기 때문에, 20 + 7 + 10 + 8 = 45초가 소요된다.

두번째 사진은 비동기(Asynchronous) 방식이다.
비동기는 작업이 동시에 진행되기 때문에, 약 20초 정도 소요된다.
(동시에 4개를 요청했다고 가정했을 경우)

그럼 작업이 동시에 들어오면, 어떤 방식으로 처리할까?




◽ 동시성 vs 병렬성

✔ 동시성 (Concurrency)

작업(Task)들이 빠르게 전환하면서 실행되어, 동시에 실행되는 것처럼 '보이는 것'

정확히 말하면 동시성이라고 동시에 실행되는 것이 아니다.

CPU에서 작업간, 문맥교환(Context Switch)이 빠르게 일어나면서 동시에 실행되는 것처럼 보인다.

📦 전제

1. 한개의 프로세스가 있다고 가정.
2. 프로세스는 총 4개의 스레드로 OS가 분할했다고 가정.
3. 비교할 CPU 환경:
	1C2T: 물리 코어 1개 + 하이퍼스레딩으로 논리 스레드 2개
	2C4T: 물리 코어 2개 + 논리 스레드 4개 (하이퍼스레딩 포함이든 아니든)

예) 싱글코어 (1C2T)

실행 가능 개수: 최대 2개 스레드 (논리적 병렬)
나머지 두개의 스레드는 대기하다가 context switch로 교체된다.

⏱️ 실행 흐름:
T1 T2 → (context switch) → T3 T4 → (반복)

원래대로는 한 코어당, 하나의 스레드만 실행이 가능하다.
하지만 요즘 CPU는 SMT(동시 멀티스레딩)을 지원해서, T1 T2가 빠르게 왔다갔다 하면서 동시에 작업되는 것처럼 보인다.

그럼 T1 T2에서 CPU가 문맥교환을 거치며 T3 T4가 작업이 된다.


예) 멀티코어 (2C4T)

실행 가능 개수: 최대 4개 스레드 (논리적 병렬)

모든 스레드를 거의 동시에 실행 가능하다.

⏱️ 실행 흐름:
T1 T2 T3 T4 (모두 동시에 실행되거나, 거의 동시에 실행됨)

문맥교환이 거의 필요 없는 것을 볼 수 있다.
CPU 자원을 더 많이 쓸 수 있음 → Idle Time 최소화 → 처리 속도 빠름


멀티코어에서 더 많이 쓰이는 이유는,
기본적으로 운영체제는 프로세스나 스레드의 작업을 시간 단위(타임슬라이스)로 나누어 CPU에 분배해, 유휴 시간(Idle Time)을 최소화한다.

유휴 시간(Idle Time): CPU가 놀고 먹는 시간

당연히 위의 예제를 보면 알겠지만, 코어가 많으면 스레드도 늘어난다.
그러니 더 빠르게 작업을 처리할 수 있다.


✔ 병렬성 (Parallelism)

물리적으로 같은 시간에 작업을 동시에 수행하는 것

병렬성은 여러 CPU나 코어를 사용하여 여러 작업을 실제로 동시에 실행한다.
이를 통해, 대규모 데이터 처리나 복잡한 계산 작업을 빠르게 처리할 수 있다.

대규모 DB의 Query 처리, 대용량 파일의 압축 해제
병렬 처리를 통해 성능을 크게 향상시킬 수 있다.


❓ 뭔가 이상한 python

"그럼 동시에 작업이 시작하니, 작업 시간이 엄청나게 줄겠네...?"

보통의 다른 언어는 이 논리가 맞지만, 파이썬은 아닐 수도 있다.

[ST]Time taken in seconds:  3.298...(생략) # 싱글 스레드 (1T)
[MT]Time taken in seconds:  3.268...(생략) # 멀티 스레드 (2T)
[MP]Time taken in seconds:  1.742...(생략) # 멀티 프로세스 (2C)

파이썬에서 좀 시간이 걸리는 작업을 각각 다른 방식으로 돌려보았다.

프로세스를 두 개를 올려놨을 경우, 속도는 엄청나게 빨라지지만...
이상하게 스레드는 한개를 쓰거나 두개를 써도 소요 시간이 크게 달라지지 않는다.

왜 그럴까?
Python은 GIL(Global Interpreter Lock)이 싱글스레드로 제한한다.

프로세스 당, 하나의 스레드만 사용이 가능하여,
멀티 스레드는 시간 소요가 싱글 스레드와 비슷한 기이한 경우가 발생한다.

GIL에 관한건 요기로!

profile
감자 그 자체

0개의 댓글