Ver3.5 멀티프로세싱과 멀티스레딩을 활용한병렬 처리 구현

멀티프로세싱과 멀티스레딩을 활용한
병렬 처리 구현
파이썬으로 진짜 빠른 코드 짜는 법 🚀 GIL부터 실전 예제까지 다 알려줌
멀티스레딩이랑 멀티프로세싱 차이가 뭔지 헷갈리는 분
병렬 처리 개념은 알겠는데 실제로 어떻게 쓰는지 모르는 분
이 글 다 읽으면 진짜 실무에서 바로 쓸 수 있는 수준까지 올라갑니다 💪
🤔 잠깐, 병렬 처리가 뭔데?
솔직히 처음 들으면 좀 어렵게 느껴지는 개념이잖아요 ㅋㅋㅋ
근데 생각해보면 우리 일상에서 이미 다 쓰고 있는 개념이에요.
예를 들어볼게요. 라면 끓이면서 계란 삶고, 동시에 김치 꺼내는 거 있잖아요?
그게 바로 병렬 처리예요. 여러 작업을 동시에 처리하는 것!
반대로 라면 다 끓이고 → 계란 삶고 → 그다음에 김치 꺼내면?
그건 순차 처리(Sequential Processing)고요.
컴퓨터도 마찬가지예요. CPU 코어가 여러 개인데 하나만 쓰면 너무 아깝잖아요?
그래서 나온 개념이 바로 멀티프로세싱이랑 멀티스레딩이에요!
스레드(Thread) : 프로세스 내에서 실행되는 작업 단위. 같은 프로세스 내 메모리를 공유함
병렬 처리(Parallel Processing) : 여러 작업을 물리적으로 동시에 실행
동시성(Concurrency) : 여러 작업이 번갈아가며 실행되어 동시처럼 보이는 것
😱 파이썬의 GIL 문제 - 이게 진짜 핵심임
파이썬 멀티스레딩 얘기할 때 절대 빠질 수 없는 게 바로 GIL(Global Interpreter Lock)이에요.
이거 모르면 나중에 "왜 멀티스레딩 써도 안 빨라지지??" 하고 멘붕 옵니다 ㅋㅋㅋ
GIL이 뭐냐면요, CPython(우리가 보통 쓰는 파이썬 인터프리터)에서
한 번에 하나의 스레드만 파이썬 바이트코드를 실행할 수 있도록 제한하는 잠금 장치예요.
즉, 스레드를 10개 만들어도 실제로 CPU 연산은 한 번에 하나씩만 된다는 거죠.
"어? 그럼 멀티스레딩 쓸 이유가 없는 거 아니야?" 라고 생각할 수 있는데,
그건 반만 맞는 말이에요!
→ 이 경우엔 멀티스레딩 써도 오히려 오버헤드 때문에 더 느려질 수 있음 😱
GIL이 별로 문제 안 되는 경우 : I/O 집약적 작업 (파일 읽기/쓰기, 네트워크 요청, DB 쿼리 등)
→ I/O 대기 중엔 GIL이 해제되어 다른 스레드가 실행 가능! 멀티스레딩 효과 있음 ✅
🧵 멀티스레딩 실전 구현 - threading 모듈
자 이제 진짜 코드 들어갑니다! 먼저 멀티스레딩부터 볼게요.
파이썬에서는 threading 모듈을 사용해요.
가장 기본적인 사용법부터 시작해서 점점 심화로 가볼게요 ㅋㅋㅋ
import threading
import time
def worker(name, delay):
"""스레드에서 실행될 함수"""
print(f"[{name}] 작업 시작!")
time.sleep(delay) # I/O 작업 시뮬레이션
print(f"[{name}] 작업 완료! ({delay}초 걸림)")
# 순차 처리 (비교용)
start = time.time()
worker("작업1", 2)
worker("작업2", 2)
worker("작업3", 2)
print(f"순차 처리 총 시간: {time.time() - start:.2f}초") # 약 6초
# 멀티스레딩 처리
start = time.time()
threads = []
for i in range(1, 4):
t = threading.Thread(target=worker, args=(f"작업{i}", 2))
threads.append(t)
t.start() # 스레드 시작!
for t in threads:
t.join() # 모든 스레드 완료 대기
print(f"멀티스레딩 총 시간: {time.time() - start:.2f}초") # 약 2초!
t.join()이 중요한데요, 이게 없으면 메인 스레드가 자식 스레드 끝나기 전에
혼자 먼저 종료해버릴 수 있어요. 꼭 써줘야 해요!
위 예제에서 순차 처리는 6초, 멀티스레딩은 약 2초 걸려요.
왜냐면 time.sleep()이 I/O 대기를 시뮬레이션하는 거라서 GIL이 해제되거든요 ㅎㅎ
import threading
import time
class DownloadThread(threading.Thread):
def __init__(self, url, name):
super().__init__()
self.url = url
self.name = name
self.result = None
def run(self):
"""스레드 실행 시 자동으로 호출되는 메서드"""
print(f"[{self.name}] {self.url} 다운로드 시작...")
time.sleep(2) # 실제로는 requests.get(self.url) 같은 코드
self.result = f"{self.url} 다운로드 완료!"
print(f"[{self.name}] 완료!")
# 스레드 생성 및 실행
threads = [
DownloadThread("https://example.com/file1.zip", "다운로더1"),
DownloadThread("https://example.com/file2.zip", "다운로더2"),
DownloadThread("https://example.com/file3.zip", "다운로더3"),
]
for t in threads:
t.start()
for t in threads:
t.join()
print(f"결과: {t.result}")
🔒 스레드 동기화 - 이거 모르면 버그 폭탄 맞음
여러 스레드가 같은 데이터에 동시에 접근하면 어떻게 될까요?
바로 경쟁 조건(Race Condition)이 발생해요!
이게 진짜 찾기 어려운 버그를 만들어내는 주범이에요 ㅠㅠ
import threading
counter = 0 # 공유 변수
def increment():
global counter
for _ in range(100000):
counter += 1 # 이 연산은 원자적이지 않음!
threads = [threading.Thread(target=increment) for _ in range(5)]
for t in threads: t.start()
for t in threads: t.join()
print(f"예상값: 500000, 실제값: {counter}")
# 실제값이 매번 다르게 나옴! 😱
import threading
counter = 0
lock = threading.Lock() # 잠금 객체 생성
def increment_safe():
global counter
for _ in range(100000):
with lock: # 잠금 획득 (자동으로 해제됨)
counter += 1
threads = [threading.Thread(target=increment_safe) for _ in range(5)]
for t in threads: t.start()
for t in threads: t.join()
print(f"예상값: 500000, 실제값: {counter}")
# 이제 항상 500000 출력! ✅
with lock: 구문을 쓰면 블록 진입 시 자동으로 잠금을 획득하고,
블록 종료 시 자동으로 해제해줘요. 예외가 발생해도 안전하게 해제됩니다!
Lock 외에도 다양한 동기화 도구들이 있어요:
| 동기화 도구 | 설명 | 사용 상황 |
|---|---|---|
threading.Lock |
기본 잠금. 한 번에 하나의 스레드만 접근 | 단순 공유 자원 보호 |
threading.RLock |
재진입 가능한 잠금. 같은 스레드가 여러 번 획득 가능 | 재귀 함수에서 잠금 필요 시 |
threading.Semaphore |
N개의 스레드까지 동시 접근 허용 | 동시 접속 수 제한 (DB 커넥션 풀 등) |
threading.Event |
스레드 간 신호 전달 | 특정 조건 충족 시 스레드 깨우기 |
threading.Condition |
조건 변수. wait/notify로 스레드 협력 | 생산자-소비자 패턴 |
import threading
import time
# 최대 3개의 스레드만 동시에 실행 허용
semaphore = threading.Semaphore(3)
def api_request(request_id):
with semaphore: # 세마포어 획득 (3개 초과 시 대기)
print(f"요청 {request_id}: API 호출 중...")
time.sleep(1) # API 응답 대기 시뮬레이션
print(f"요청 {request_id}: 완료!")
# 10개의 요청을 동시에 보내지만 최대 3개만 동시 실행
threads = [threading.Thread(target=api_request, args=(i,)) for i in range(10)]
for t in threads: t.start()
for t in threads: t.join()
⚡ ThreadPoolExecutor - 이게 진짜 실무에서 많이 씀
스레드를 직접 만들고 관리하는 건 좀 번거롭잖아요?
그래서 파이썬 3.2부터 concurrent.futures 모듈이 생겼어요!
ThreadPoolExecutor를 쓰면 스레드 풀을 쉽게 관리할 수 있어요 ㅎㅎ
from concurrent.futures import ThreadPoolExecutor, as_completed
import time
def fetch_data(url):
"""URL에서 데이터 가져오기 시뮬레이션"""
time.sleep(1) # 네트워크 요청 시뮬레이션
return f"{url}의 데이터"
urls = [f"https://api.example.com/data/{i}" for i in range(10)]
# 방법 1: map() 사용 - 순서 보장
with ThreadPoolExecutor(max_workers=5) as executor:
results = list(executor.map(fetch_data, urls))
print("모든 결과:", results)
# 방법 2: submit() + as_completed() - 완료 순서대로 처리
with ThreadPoolExecutor(max_workers=5) as executor:
future_to_url = {executor.submit(fetch_data, url): url for url in urls}
for future in as_completed(future_to_url):
url = future_to_url[future]
try:
result = future.result()
print(f"완료: {url} → {result}")
except Exception as e:
print(f"오류 발생: {url} → {e}")
as_completed()는 완료된 순서대로 결과를 반환해줘요.
빠른 작업 결과를 먼저 처리하고 싶을 때 유용해요!
with 블록을 벗어나면 자동으로 모든 스레드가 완료될 때까지 기다려줘요.
join() 안 써도 되니까 훨씬 편하죠 ㅋㅋㅋ
🚀 멀티프로세싱 실전 구현 - multiprocessing 모듈
이제 진짜 CPU 코어를 여러 개 쓰는 멀티프로세싱이에요!
CPU 집약적인 작업 (이미지 처리, 데이터 분석, 머신러닝 전처리 등)에서
진짜 위력을 발휘해요 💪
API는 threading이랑 거의 비슷해서 배우기 쉬워요!
import multiprocessing
import time
import os
def cpu_heavy_task(n):
"""CPU 집약적 작업 시뮬레이션"""
print(f"프로세스 ID: {os.getpid()}, 작업 {n} 시작")
result = sum(i * i for i in range(10**7)) # 무거운 계산
print(f"프로세스 ID: {os.getpid()}, 작업 {n} 완료")
return result
if __name__ == "__main__": # 이 부분 중요!! Windows에서 필수!
# 순차 처리
start = time.time()
results = [cpu_heavy_task(i) for i in range(4)]
print(f"순차 처리: {time.time() - start:.2f}초")
# 멀티프로세싱
start = time.time()
processes = []
for i in range(4):
p = multiprocessing.Process(target=cpu_heavy_task, args=(i,))
processes.append(p)
p.start()
for p in processes:
p.join()
print(f"멀티프로세싱: {time.time() - start:.2f}초")
# CPU 코어 수에 따라 4배 가까이 빨라질 수 있음!
이 가드가 없으면 무한히 새 프로세스를 생성하는 무한 루프에 빠져버려요 😱
Linux/Mac은 fork() 방식이라 상관없지만, 크로스 플랫폼 코드를 위해 항상 써주는 게 좋아요!
📦 프로세스 간 데이터 공유 - 이게 멀티프로세싱의 핵심 난이도
멀티스레딩은 메모리를 공유하지만, 멀티프로세싱은 각 프로세스가 독립된 메모리를 가져요.
그래서 데이터를 주고받으려면 특별한 방법이 필요해요!
import multiprocessing
import time
def producer(queue, items):
"""데이터를 생성해서 큐에 넣는 프로세스"""
for item in items:
print(f"생산: {item}")
queue.put(item)
time.sleep(0.5)
queue.put(None) # 종료 신호
def consumer(queue):
"""큐에서 데이터를 꺼내 처리하는 프로세스"""
while True:
item = queue.get()
if item is None: # 종료 신호 받으면 종료
break
print(f"소비: {item} 처리 완료!")
if __name__ == "__main__":
queue = multiprocessing.Queue()
items = ["데이터1", "데이터2", "데이터3", "데이터4", "데이터5"]
p1 = multiprocessing.Process(target=producer, args=(queue, items))
p2 = multiprocessing.Process(target=consumer, args=(queue,))
p1.start()
p2.start()
p1.join()
p2.join()
print("생산자-소비자 패턴 완료!")
import multiprocessing
def update_counter(counter, lock, n):
for _ in range(n):
with lock:
counter.value += 1
if __name__ == "__main__":
# 공유 메모리 변수 생성 (타입코드 'i' = 정수)
counter = multiprocessing.Value('i', 0)
lock = multiprocessing.Lock()
processes = [
multiprocessing.Process(target=update_counter, args=(counter, lock, 10000))
for _ in range(4)
]
for p in processes: p.start()
for p in processes: p.join()
print(f"최종 카운터: {counter.value}") # 40000
# 배열 공유
shared_array = multiprocessing.Array('d', [1.0, 2.0, 3.0, 4.0, 5.0])
print(f"공유 배열: {list(shared_array)}")
🏊 ProcessPoolExecutor - 멀티프로세싱도 풀로 편하게!
ThreadPoolExecutor처럼 프로세스도 풀로 관리할 수 있어요!
API가 완전히 동일해서 스레드 ↔ 프로세스 전환이 엄청 쉬워요 ㅋㅋㅋ
from concurrent.futures import ProcessPoolExecutor, as_completed
import time
def heavy_computation(n):
"""CPU 집약적 계산"""
return sum(i ** 2 for i in range(n))
if __name__ == "__main__":
numbers = [10**6, 2*10**6, 3*10**6, 4*10**6, 5*10**6]
# 순차 처리
start = time.time()
results = [heavy_computation(n) for n in numbers]
print(f"순차 처리: {time.time() - start:.2f}초")
# ProcessPoolExecutor (CPU 코어 수만큼 자동 설정)
start = time.time()
with ProcessPoolExecutor() as executor:
results = list(executor.map(heavy_computation, numbers))
print(f"멀티프로세싱: {time.time() - start:.2f}초")
# 결과 확인
print(f"결과 샘플: {results[0]}")
max_workers = os.cpu_count() (코어 수와 동일하게)I/O 집약 작업 :
max_workers = os.cpu_count() * 2~5 (코어 수보다 많이 설정 가능)I/O 작업은 대기 시간이 많아서 스레드가 코어 수보다 많아도 효율적이에요!
CPU 작업은 코어 수 이상으로 늘려봤자 오버헤드만 증가해요.
🎯 실전 예제 1 - 웹 크롤링 (멀티스레딩)
실무에서 가장 많이 쓰는 케이스 중 하나예요!
여러 URL에서 데이터를 동시에 가져오는 거죠.
재능넷 같은 플랫폼에서도 여러 API를 동시에 호출할 때 이런 패턴을 써요 ㅎㅎ
import requests
from concurrent.futures import ThreadPoolExecutor, as_completed
import time
def fetch_url(url):
"""URL에서 데이터 가져오기"""
try:
response = requests.get(url, timeout=10)
response.raise_for_status()
return {
"url": url,
"status": response.status_code,
"length": len(response.content),
"success": True
}
except requests.RequestException as e:
return {
"url": url,
"error": str(e),
"success": False
}
def crawl_urls(urls, max_workers=10):
"""여러 URL을 병렬로 크롤링"""
results = []
with ThreadPoolExecutor(max_workers=max_workers) as executor:
future_to_url = {executor.submit(fetch_url, url): url for url in urls}
for future in as_completed(future_to_url):
result = future.result()
results.append(result)
if result["success"]:
print(f"✅ {result['url']}: {result['length']} bytes")
else:
print(f"❌ {result['url']}: {result['error']}")
return results
if __name__ == "__main__":
urls = [
"https://httpbin.org/delay/1",
"https://httpbin.org/delay/2",
"https://httpbin.org/delay/1",
"https://httpbin.org/get",
"https://httpbin.org/ip",
]
start = time.time()
results = crawl_urls(urls, max_workers=5)
elapsed = time.time() - start
success_count = sum(1 for r in results if r["success"])
print(f"\n완료! {success_count}/{len(urls)} 성공, 소요 시간: {elapsed:.2f}초")
🎯 실전 예제 2 - 이미지 처리 (멀티프로세싱)
이미지 처리는 CPU를 엄청 많이 써요.
수백 장의 이미지를 리사이즈하거나 필터 적용할 때 멀티프로세싱이 진짜 빛을 발해요!
from PIL import Image
from concurrent.futures import ProcessPoolExecutor
import os
import time
from pathlib import Path
def process_image(args):
"""이미지 처리 함수 (리사이즈 + 그레이스케일)"""
input_path, output_path, size = args
try:
with Image.open(input_path) as img:
# 리사이즈
img_resized = img.resize(size, Image.LANCZOS)
# 그레이스케일 변환
img_gray = img_resized.convert('L')
# 저장
img_gray.save(output_path, quality=85)
return f"✅ {input_path} → {output_path}"
except Exception as e:
return f"❌ {input_path}: {e}"
def batch_process_images(input_dir, output_dir, size=(800, 600)):
"""디렉토리 내 모든 이미지 병렬 처리"""
input_path = Path(input_dir)
output_path = Path(output_dir)
output_path.mkdir(exist_ok=True)
# 처리할 이미지 목록 생성
image_files = list(input_path.glob("*.jpg")) + list(input_path.glob("*.png"))
if not image_files:
print("처리할 이미지가 없습니다!")
return
# 인자 리스트 생성
args_list = [
(str(img), str(output_path / img.name), size)
for img in image_files
]
print(f"총 {len(image_files)}개 이미지 처리 시작...")
start = time.time()
# CPU 코어 수만큼 프로세스 사용
with ProcessPoolExecutor(max_workers=os.cpu_count()) as executor:
results = list(executor.map(process_image, args_list))
elapsed = time.time() - start
for result in results:
print(result)
print(f"\n처리 완료! 소요 시간: {elapsed:.2f}초")
print(f"평균 처리 속도: {len(image_files)/elapsed:.1f}장/초")
if __name__ == "__main__":
batch_process_images("./input_images", "./output_images")
🎯 실전 예제 3 - 혼합 패턴 (멀티프로세싱 + 멀티스레딩)
실제 복잡한 시스템에서는 두 가지를 같이 쓰는 경우도 많아요!
예를 들어 여러 CPU 코어에서 각각 여러 I/O 작업을 처리하는 패턴이죠.
이걸 하이브리드 병렬 처리라고 해요.
import multiprocessing
from concurrent.futures import ThreadPoolExecutor
import time
import requests
def fetch_batch(urls):
"""하나의 프로세스에서 여러 URL을 스레드로 처리"""
def fetch(url):
try:
# 실제로는 requests.get(url) 사용
time.sleep(0.5) # 네트워크 요청 시뮬레이션
return f"{url}: 성공"
except Exception as e:
return f"{url}: 실패 - {e}"
# 프로세스 내에서 스레드 풀 사용
with ThreadPoolExecutor(max_workers=5) as executor:
results = list(executor.map(fetch, urls))
return results
def split_list(lst, n):
"""리스트를 n개로 균등 분할"""
k, m = divmod(len(lst), n)
return [lst[i*k+min(i,m):(i+1)*k+min(i+1,m)] for i in range(n)]
if __name__ == "__main__":
# 100개의 URL 처리
all_urls = [f"https://api.example.com/item/{i}" for i in range(100)]
cpu_count = multiprocessing.cpu_count()
url_batches = split_list(all_urls, cpu_count)
print(f"CPU 코어: {cpu_count}개, 배치당 URL: {len(url_batches[0])}개")
start = time.time()
with multiprocessing.Pool(processes=cpu_count) as pool:
all_results = pool.map(fetch_batch, url_batches)
# 결과 평탄화
flat_results = [item for batch in all_results for item in batch]
print(f"총 {len(flat_results)}개 처리 완료!")
print(f"소요 시간: {time.time() - start:.2f}초")
📊 성능 비교 및 프로파일링
"그래서 얼마나 빨라지는데?" 라는 질문에 답하려면 직접 측정해봐야 해요!
무작정 병렬화한다고 다 빨라지는 게 아니거든요 ㅋㅋㅋ
오버헤드가 더 클 수도 있어요!
import time
import functools
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
import os
def benchmark(func, *args, **kwargs):
"""함수 실행 시간 측정 데코레이터"""
start = time.perf_counter()
result = func(*args, **kwargs)
elapsed = time.perf_counter() - start
return result, elapsed
def compare_approaches(task_func, data_list, label="작업"):
"""순차, 멀티스레딩, 멀티프로세싱 성능 비교"""
print(f"\n{'='*50}")
print(f"📊 {label} 성능 비교 ({len(data_list)}개 항목)")
print(f"{'='*50}")
# 1. 순차 처리
_, seq_time = benchmark(lambda: [task_func(d) for d in data_list])
print(f"순차 처리: {seq_time:.3f}초")
# 2. 멀티스레딩
def thread_run():
with ThreadPoolExecutor(max_workers=os.cpu_count()*2) as ex:
return list(ex.map(task_func, data_list))
_, thread_time = benchmark(thread_run)
print(f"멀티스레딩: {thread_time:.3f}초 (속도향상: {seq_time/thread_time:.2f}x)")
# 3. 멀티프로세싱
def process_run():
with ProcessPoolExecutor(max_workers=os.cpu_count()) as ex:
return list(ex.map(task_func, data_list))
_, proc_time = benchmark(process_run)
print(f"멀티프로세싱: {proc_time:.3f}초 (속도향상: {seq_time/proc_time:.2f}x)")
print(f"\n🏆 최적 방법: ", end="")
times = {"순차": seq_time, "스레딩": thread_time, "프로세싱": proc_time}
best = min(times, key=times.get)
print(f"{best} ({times[best]:.3f}초)")
# CPU 집약 작업 테스트
def cpu_task(n):
return sum(i**2 for i in range(n))
# I/O 집약 작업 테스트
def io_task(n):
time.sleep(0.1)
return n * 2
if __name__ == "__main__":
compare_approaches(cpu_task, [500000]*8, "CPU 집약 작업")
compare_approaches(io_task, list(range(20)), "I/O 집약 작업")
⚠️ 자주 하는 실수들 - 이것만 피해도 반은 성공
1000개 작업에 1000개 스레드 만들면 오히려 더 느려질 수 있어요!
→ 해결책: 풀(Pool)을 사용하고 적절한 max_workers 설정
multiprocessing은 함수를 pickle로 직렬화해서 전달해요.람다나 중첩 함수는 pickle이 안 돼서 오류가 발생해요!
→ 해결책: 모듈 레벨의 일반 함수 사용
→ 해결책: 잠금 획득 순서를 일관되게 유지, timeout 설정, RLock 사용
→ 해결책: return 값 사용, Queue, Manager 사용
if __name__ == "__main__": 가드 사용 (멀티프로세싱)✅ 풀(Pool/Executor) 사용으로 리소스 관리
✅
with 구문으로 자동 정리✅ 예외 처리 반드시 포함
✅ 실제 성능은 프로파일링으로 측정 후 결정
✅ 작은 작업은 오버헤드 때문에 순차 처리가 더 빠를 수 있음
✅ 공유 상태 최소화 (불변 데이터 선호)
🔥 Python 3.12+ 새로운 기능 - GIL 드디어 없어진다?!
이거 진짜 핫한 소식인데요 ㅋㅋㅋ
Python 3.12에서 PEP 703 - Making the Global Interpreter Lock Optional이 승인됐어요!
Python 3.13부터는 실험적으로 GIL 없는 빌드를 사용할 수 있어요.
python3.13t (t = free-threaded) 빌드를 쓰면 GIL 없이 진짜 멀티스레딩이 가능해요!
아직 실험적 단계라 프로덕션에 바로 쓰기는 좀 이르지만,
앞으로 파이썬 멀티스레딩의 판도가 완전히 바뀔 수도 있어요!
# GIL 비활성화 확인
import sys
print(sys._is_gil_enabled()) # False면 GIL 비활성화됨
# 환경 변수로 GIL 비활성화 (Python 3.13+)
# PYTHON_GIL=0 python3.13t your_script.py
🎓 마무리 - 어떤 걸 선택해야 할까?
지금까지 멀티스레딩이랑 멀티프로세싱에 대해 엄청 많이 알아봤는데요!
마지막으로 실무에서 바로 쓸 수 있는 의사결정 가이드를 정리해드릴게요 😊
재능넷에서 파이썬 관련 프리랜서 작업을 하거나 의뢰할 때도
이런 병렬 처리 개념을 알고 있으면 훨씬 더 효율적인 코드를 만들 수 있어요!
| 상황 | 추천 방법 | 이유 |
|---|---|---|
| 웹 크롤링, API 다중 호출 | ThreadPoolExecutor | I/O 대기 중 GIL 해제, 가벼운 오버헤드 |
| 이미지/영상 처리 | ProcessPoolExecutor | CPU 집약, 진짜 병렬 실행 필요 |
| 데이터 분석 (pandas, numpy) | ProcessPoolExecutor | CPU 집약, 대용량 데이터 분할 처리 |
| DB 다중 쿼리 | ThreadPoolExecutor | I/O 집약, 커넥션 풀과 함께 사용 |
| 실시간 채팅/소켓 | asyncio | 이벤트 루프 기반, 수천 개 연결 처리 |
| 머신러닝 데이터 전처리 | ProcessPoolExecutor | CPU 집약, 독립적 배치 처리 |
| 파일 시스템 작업 | ThreadPoolExecutor | I/O 집약, 간단한 구현 |
멀티스레딩 : I/O 집약 작업에 효과적. threading, ThreadPoolExecutor 사용
멀티프로세싱 : CPU 집약 작업에 효과적. multiprocessing, ProcessPoolExecutor 사용
동기화 : Lock, Semaphore, Event 등으로 공유 자원 보호
IPC : Queue, Pipe, Value, Array로 프로세스 간 통신
성능 측정 : 항상 프로파일링으로 실제 효과 확인 후 적용
관련 키워드
댓글 작성
이 글에 대한 여러분의 생각을 들려주세요
로그인이 필요합니다
댓글을 작성하려면 먼저 로그인해주세요.
댓글 0
지식인의 숲 - 지적 재산권 보호 고지
지적 재산권 보호 고지
- 저작권 및 소유권: 본 컨텐츠는 재능넷의 독점 AI 기술로 생성되었으며, 대한민국 저작권법 및 국제 저작권 협약에 의해 보호됩니다.
- AI 생성 컨텐츠의 법적 지위: 본 AI 생성 컨텐츠는 재능넷의 지적 창작물로 인정되며, 관련 법규에 따라 저작권 보호를 받습니다.
- 사용 제한: 재능넷의 명시적 서면 동의 없이 본 컨텐츠를 복제, 수정, 배포, 또는 상업적으로 활용하는 행위는 엄격히 금지됩니다.
- 데이터 수집 금지: 본 컨텐츠에 대한 무단 스크래핑, 크롤링, 및 자동화된 데이터 수집은 법적 제재의 대상이 됩니다.
- AI 학습 제한: 재능넷의 AI 생성 컨텐츠를 타 AI 모델 학습에 무단 사용하는 행위는 금지되며, 이는 지적 재산권 침해로 간주됩니다.
아직 댓글이 없습니다.