본문으로 바로가기

동시성 vs 병렬성

같은 작업 3개를 코어 1개의 시분할과 코어 2개의 동시 실행에 나란히 흘려, '겹쳐 다루기'와 '같은 순간 실행'의 차이를 보여준다.

1 / 12작업 A(4)·B(3)·C(3), 총 10단위를 두 방식으로 처리합니다. 위는 코어 1개가 틱마다 작업을 갈아타고(동시성), 아래는 코어 2개가 같은 틱에 두 작업을 진행합니다(병렬성).
동시성 — 코어 1개, 시분할
코어 1: 대기
A0/4
B0/3
C0/3
병렬성 — 코어 2개, 동시 실행
코어 1: 대기코어 2: 대기
A0/4
B0/3
C0/3
단계 목록 · 키보드 ←/→ 이동, Space 재생

동시성은 여러 일을 '겹쳐 다루는 구조'(코어 1개로도 가능), 병렬성은 여러 일이 '같은 순간에 실제로 실행'되는 것(코어가 여러 개여야 가능)이다.

왜 헷갈리는가

"파이썬은 GIL 때문에 병렬이 안 된다는데 왜 asyncio는 잘 돌아요?" — 동시성과 병렬성을 같은 것으로 취급해서 나오는 질문이다. asyncio는 코어 1개 위의 동시성이고, GIL이 막는 것은 스레드의 병렬성이다.

애니메이션이 보여주는 것

위 패널의 코어 1개는 틱마다 작업을 갈아탄다(라운드 로빈). 세 작업이 모두 '진행 중'이지만 어느 순간을 잘라 봐도 실행 중인 것은 하나다 — 이것이 동시성이다. 겹쳐 보이는 것은 시간을 잘게 나눈 착시다.

아래 패널의 코어 2개는 같은 틱에 두 작업이 실제로 함께 진행된다 — 이것이 병렬성이다. 같은 총량(10단위)을 절반의 시간에 끝내는 것이 화면의 완료 틱으로 확인된다.

왜 구분이 실무에서 중요한가

무엇이 병목인지에 따라 동시성이 답일 수도, 병렬성이 답일 수도 있다. 반대로 고르면 복잡도만 얻고 성능은 그대로다.

  • I/O 바운드(API 호출, DB 대기): 코어는 놀고 있다. 동시성(async/await, 이벤트 루프, 스레드)만으로 처리량이 뛴다. 코어를 늘려도 소용없다.
  • CPU 바운드(인코딩, 암호화, 수치 계산): 코어가 꽉 차 있다. 병렬성(멀티프로세스, 멀티코어, SIMD)이 필요하다. async로 바꿔도 빨라지지 않는다.
  • Go의 고루틴, Java의 가상 스레드: 동시성 구조(수만 개의 겹친 작업)를 소수 코어의 병렬 실행 위에 얹은 것 — 둘은 배타가 아니라 직교한다.
  • 파이썬 GIL: 스레드 동시성은 허용하되 같은 프로세스 안의 병렬 실행을 막는다. 그래서 CPU 바운드는 multiprocessing으로 우회한다.

한 문장 정의의 출처

Rob Pike의 표현을 빌리면 "동시성은 많은 일을 한꺼번에 다루는 것(dealing with), 병렬성은 많은 일을 한꺼번에 하는 것(doing)"이다. 동시성은 프로그램의 구조에 대한 이야기고, 병렬성은 실행의 물리적 사실이다.

기억할 것

  • 동시성 = 구조(겹쳐 다루기), 병렬성 = 실행(같은 순간에 실제로).
  • 코어 1개로도 동시성은 되지만 병렬성은 안 된다.
  • I/O 바운드는 동시성으로, CPU 바운드는 병렬성으로 푼다.
  • 둘은 직교한다 — 고루틴처럼 동시성 구조를 병렬 실행 위에 얹을 수 있다.
동시성 vs 병렬성 | KIE