프로세스 vs 스레드 · 동시성 기초
프로세스는 독립 아파트, 스레드는 같은 아파트의 가족 — 컨텍스트 스위칭·데드락·레이스.
'Node는 싱글 스레드인데 왜 빠른가', '왜 CPU 집약적 작업엔 Worker가 필요한가', 'dev 서버 여러 개 떠있는데 뭘 죽여야 하나' 답이 여기 있다.
프로세스는 독립된 아파트다. 자기만의 메모리 공간(주소 공간)·파일 디스크립터·환경변수를 가진다. 옆 프로세스는 내 메모리를 못 본다(OS가 격리). 스레드는 같은 아파트 안의 가족 구성원 — 프로세스의 메모리를 공유하며 동시에 실행되는 실행 흐름.
| 구분 | 프로세스 | 스레드 |
|---|---|---|
| 메모리 | 독립 | 공유 |
| 생성 비용 | 무거움 (수 ms) | 가벼움 (수 µs) |
| 통신 | IPC (파이프·소켓·공유메모리) | 변수 직접 공유 |
| 하나 죽어도 | 다른 프로세스 생존 | 프로세스 전체 크래시 위험 |
| 대표 예 | Chrome 탭마다 별도 프로세스 | Node Worker·Python Thread |
컨텍스트 스위칭(context switching) — CPU는 1개인데 프로세스/스레드는 수백 개. OS가 수 ms마다 레지스터·PC를 저장·복원하며 번갈아 실행한다. 스위칭 자체가 비용이므로 스레드가 너무 많으면 오히려 느려진다.
// Node.js는 "싱글 스레드 이벤트 루프" (ch07-5 이벤트 루프 참고)
// 하지만 내부적으로 libuv 스레드풀이 I/O·crypto 일부를 병렬 처리
// CPU 집약적 작업은 Worker Thread로 분리
const { Worker } = require("worker_threads");
// 무거운 계산을 서브 스레드에
const worker = new Worker("./heavy-job.js");
worker.on("message", (result) => console.log(result));
worker.postMessage({ data: [...] });
// Python의 GIL — 진짜 병렬이 아닌 협력 (CPython 기준)
// → CPU 집약적이면 multiprocessing 모듈로 프로세스 분리Node·Python의 '싱글 스레드'는 JS/파이썬 코드가 한 스레드라는 뜻. I/O·네트워크는 내부 스레드풀이 처리해 비동기가 가능하다.
동시성(Concurrency) ≠ 병렬(Parallelism). 동시성은 '여러 일이 번갈아 진행됨(실제로는 싱글 코어라도 됨)', 병렬은 '진짜로 동시에 여러 코어에서 실행'. Node 이벤트 루프는 동시성, Worker Thread는 병렬.
공유 자원의 문제 2종. ① 레이스 컨디션 — 두 스레드가 같은 변수를 동시에 수정해 결과가 뒤죽박죽. ② 데드락 — A는 B 락을 기다리고 B는 A 락을 기다리며 영원히 멈춤. 해법: 락(Mutex)·아토믹 연산·메시지 패싱.
# 실전: 시스템 프로세스·스레드 감시
ps -eo pid,user,pcpu,pmem,command --sort=-pcpu | head # CPU 많이 쓰는 프로세스 (Linux)
top # 실시간 (q로 종료)
htop # 예쁜 버전 (brew install htop)
# Node 앱의 스레드 수
ps -eLf | grep node | wc -l
# Windows
Get-Process -Name node | Select-Object Id, Threads"dev 서버가 느려요" 상황에서 top으로 어느 프로세스가 CPU를 먹는지 확인 → kill. ch14-4와 콤보.
스레드는 메모리를 공유하므로 하나가 크래시해도 다른 스레드는 안전하다.
두 스레드가 같은 변수를 동시에 수정해서 결과가 예측 불가해지는 현상의 이름은?