OS: MAC
참고: 👉 점프 투 파이썬 - 라이브러리 예제 바로가기
066. 스레드를 이용하여 병렬로 처리하려면? ― threading(쓰레딩)
| 구분 | 설명 | 예시 | 결과 |
| 스레드(Thread)란? | 하나의 프로세스 안에서 동시에 여러 작업을 수행하도록 만들어진 실행 흐름 단위 | 여러 작업을 하나씩 처리하면 느린데, 스레드를 쓰면 동시에 실행되는 것처럼 보임 |
I/O 중심 작업에서 속도 개선 |
| threading 모듈 소개 | 파이썬에서 스레드를 만들고 관리하는 기본 모듈 | import threading | 스레드를 쉽게 생성하고 시작할 수 있음 |
| 스레드 생성하기 |
Thread(target=함수명) 형태로 스레드를 만들고 start()로 실행 |
import threading def worker(): print("작업 중...") th = threading.Thread(target=worker) th.start() |
작업 중... # 메인 흐름과 동시에 실행 |
| 인자 전달하기 |
Thread(target, args=(값,)) 방식으로 함수에 인자 전달 가능 |
import threading import time def worker(n): print(f"{n}초 작업 시작") time.sleep(n) print(f"{n}초 작업 끝!") thread = threading.Thread(target=worker, args=(3,)) thread.start() |
# 스레드 함수가 전달받은 값으로 동작 3초 작업 시작 >>> 3초 작업 끝! |
| 여러 스레드 실행 |
반복문을 이용하여 여러 개의 스레드 생성 |
import threading import time def worker(n): print(f"{n}번 작업 시작") time.sleep(1) print(f"{n}번 작업 끝") threads = [] for i in range(3): t = threading.Thread(target=worker, args=(i,)) t.start() threads.append(t) for t in threads: t.join() |
# 3개의 스레드가 동시에 실행 0번 작업 시작 1번 작업 시작 2번 작업 시작 0번 작업 끝 2번 작업 끝 1번 작업 끝 |
| join() | 모든 스레드가 끝날 때까지 기다리는 기능 |
import threading import time def worker(n): print(f"{n}번 작업 시작") time.sleep(1) print(f"{n}번 작업 끝") threads = [] for i in range(3): t = threading.Thread(target=worker, args=(i,)) t.start() threads.append(t) for t in threads: t.join() print("모든 작업 완료!") |
# 메인 프로그램이 스레드 종료까지 대기 0번 작업 시작 1번 작업 시작 2번 작업 시작 2번 작업 끝 0번 작업 끝 1번 작업 끝 모든 작업 완료! |
| 스레드 vs 프로세스 | 스레드는 메모리를 공유하기 때문에 가볍지만,GIL 영향으로 CPU 작업에는 비효율적 | CPU 작업은 multiprocessing 사용 권장 | I/O 작업에서 강함 |
| 스레드 안전 문제(lock) | 여러 스레드가 같은 데이터 접근 시 충돌 가능 → Lock() 필요 | import threading import time # 공유 자원 counter = 0 lock = threading.Lock() def worker(): global counter for _ in range(100000): with lock: # 🔒 락을 획득한 상태에서만 counter 변경 counter += 1 threads = [] # 스레드 3개 실행 for i in range(3): t = threading.Thread(target=worker) t.start() threads.append(t) # 모든 스레드를 기다림 for t in threads: t.join() print("최종 counter 값:", counter) |
# 데이터 꼬임 방지 최종 counter 값: 300000 |
라이브러리 예제 문제:

문제를 푸는데 오류가 난다. 이전에 작성했던 파일 이름이 충돌해서 그렇다고 한다.
import urllib.request 안에서 내부적으로 import hashlib 를 하는데
→ 원래는 파이썬 표준 라이브러리 hashlib 를 불러야 하는데
→ 내가 만든 hashlib.py가 먼저 잡혀버림 → 그래서 AttributeError가 발생
이것 먼저 해결하자.
1) 내가 만든 hashlib.py의 파일명을 my_hashlib.py로 변경해준다.
2) __pycache__ 폴더 내 hashlib.cpython-313.pyc 파일도 삭제한다.
3) 파이썬 셸을 완전히 끈다.
4) 터미널 > 파이썬 셸을 킨다.
5) 아래 코드를 입력한다. 아무것도 안나와야 문제를 풀 수 있다.
| import urllib.request, urllib.error |

067. 멀티 프로세스를 이용하여 병렬로 처리하려면? ― multiprocessing
- 멀티프로세싱은 REPL(대화형 셸)보다는 반드시 .py 파일로 만들어서 터미널에서 실행하는 방식이 가장 안정적.
| 구분 | 설명 | 예시 | 결과 |
| 멀티프로세싱이란? | 하나의 프로그램 안에서 프로세스를 여러 개 만들어서 동시에 작업을 처리하는 방법. GIL의 영향을 받지 않아서 CPU를 빡세게 쓰는 작업에 유리함. | 큰 리스트에서 소수 판별, 행렬 계산, 이미지 변환 등 | 코어 여러 개를 활용해서 속도 향상 |
| 기본 모듈 | multiprocessing 모듈을 사용하여 프로세스를 만들고 제어함. |
from multiprocessing import Process | Process(target=함수) 형태로 프로세스를 생성 |
| Process 사용 |
Process(target=함수, args=(인자,)) 로 프로세스를 만들고 start()로 실행, join()으로 종료까지 대기 |
# mp_test.py로 생성 from multiprocessing import Process import time def worker(n): print(f"{n} 작업 시작") time.sleep(1) print(f"{n} 작업 끝") if __name__ == "__main__": p = Process(target=worker, args=(3,)) p.start() p.join() # 터미널에서 이 파일 실행: python mp_test.py |
3 작업 시작 3 작업 끝 |
| 여러 프로세스 실행 |
리스트에 모아 두고 반복문으로 start(), 이후 다시 반복문으로 join() |
# mp_test2.py로 생성 from multiprocessing import Process import time def worker(n): print(f"{n} 작업 시작") time.sleep(1) print(f"{n} 작업 끝") if __name__ == "__main__": nums = [1, 2, 3, 4] processes = [] for n in nums: p = Process(target=worker, args=(n,)) p.start() processes.append(p) for p in processes: p.join() print("모든 작업 완료") # 터미널에서 이 파일 실행: python mp_test2.py |
# 여러 CPU 코어에서 동시에 worker 실행 1 작업 시작 2 작업 시작 4 작업 시작 3 작업 시작 1 작업 끝 4 작업 끝 2 작업 끝 3 작업 끝 모든 작업 완료 |
| 메인 가드 필요 |
맥·윈도우에서는 프로세스 생성 코드가 반드시 if __name__ == "__main__" 안에 있어야 함. |
if __name__ == "__main__": main() |
안 그러면 프로세스가 무한 생성되거나 에러 발생. |
| 프로세스 간 데이터 공유 | 프로세스는 메모리를 공유하지 않음. 값을 주고받으려면 Queue, Pipe, Manager 등을 사용해야 함. | # mp_test3.py로 생성 from multiprocessing import Process, Queue def worker(q, n): q.put(n*n) if __name__ == "__main__": q = Queue() nums = [1, 2, 3] procs = [] for n in nums: p = Process(target=worker, args=(q, n)) p.start() procs.append(p) for p in procs: p.join() while not q.empty(): print(q.get()) # 터미널에서 이 파일 실행: python mp_test3.py |
# 각 프로세스가 큐를 통해 메시지/데이터 주고받음 1 4 9 |
| Pool을 이용한 작업 분배(동시작업) | 같은 함수를 여러 데이터에 적용할 때는 Pool이 편리함. 프로세스 풀을 만들고 map, apply 등 사용. | # mp_test4.py로 생성 from multiprocessing import Pool import time def square(n): time.sleep(1) return n * n if __name__ == "__main__": nums = [1, 2, 3, 4] with Pool(4) as pool: results = pool.map(square, nums) print(results) |
# 리스트 nums의 각 원소에 대해 worker가 병렬 실행 [1, 4, 9, 16] |
| 쓰레드와 차이 |
threading은 메모리 공유, GIL 때문에 CPU 작업엔 제한적. multiprocessing은 프로세스별 독립 메모리 + GIL 우회. 대신 생성/통신 비용이 더 큼. |
CPU 바운드 → multiprocessing / I/O 바운드 → threading |
올바른 선택으로 성능 최적화 |
라이브러리 예제 문제:

