본문 바로가기

프로그래밍 언어/파이썬

파이썬 병행성 다루기, 멀티스레딩과 멀티프로세싱

파이썬에서 병행성 다루기, 멀티스레딩과 멀티프로세싱

파이썬(Python)은 병행성(concurrency)을 다룰 수 있는 다양한 도구와 방법을 제공합니다. 멀티스레딩과 멀티프로세싱은 특히 중요한 두 가지 기법으로, 각각 다른 환경과 요구에 따라 활용될 수 있습니다. 이 가이드에서는 파이썬에서 멀티스레딩과 멀티프로세싱을 어떻게 효과적으로 사용할 수 있는지에 대해 깊이 있게 탐구합니다.

 

 

목차

  1. 병행성의 기본 개념
  2. 멀티스레딩
  3. 멀티프로세싱
  4. 멀티스레딩과 멀티프로세싱 비교
  5. 실전 예제
  6. 주의사항과 최적화

 

병행성의 기본 개념

병행성은 프로그램이 여러 작업을 동시에 수행하는 능력을 의미합니다. 파이썬에서는 스레드프로세스를 사용해 병행성을 구현할 수 있습니다. 병행성을 통해 작업의 처리 속도를 높이거나, 여러 작업을 병렬로 실행해 시스템 자원을 효율적으로 사용할 수 있습니다.

 

 

멀티스레딩

멀티스레딩은 하나의 프로세스 내에서 여러 스레드를 생성해 병행 작업을 처리하는 방법입니다. 파이썬에서는 threading 모듈을 사용해 멀티스레딩을 구현할 수 있습니다. 멀티스레딩은 I/O 작업이 많은 프로그램에서 유용하지만, 파이썬의 GIL(Global Interpreter Lock) 때문에 CPU 바운드 작업에서는 성능 이점이 제한적일 수 있습니다.

import threading

def print_numbers():
    for i in range(1, 6):
        print(i)

def print_letters():
    for letter in 'abcde':
        print(letter)

t1 = threading.Thread(target=print_numbers)
t2 = threading.Thread(target=print_letters)

t1.start()
t2.start()

t1.join()
t2.join()

위의 예제는 두 개의 스레드를 생성해 숫자와 문자를 동시에 출력하는 간단한 멀티스레딩 구현입니다. 각 스레드는 독립적으로 실행되며, join 메서드를 사용해 모든 스레드가 완료될 때까지 메인 프로그램이 기다리도록 합니다.

 

 

멀티프로세싱

멀티프로세싱은 여러 프로세스를 생성해 병행 작업을 처리하는 방법입니다. 파이썬의 multiprocessing 모듈을 사용하면, 각 프로세스가 독립적인 메모리 공간에서 실행되기 때문에 GIL의 영향을 받지 않으며, CPU 바운드 작업에 적합합니다.

import multiprocessing

def square(n):
    return n * n

if __name__ == "__main__":
    with multiprocessing.Pool(4) as pool:
        results = pool.map(square, [1, 2, 3, 4, 5])
    print(results)

이 예제는 multiprocessing.Pool을 사용해 네 개의 프로세스를 병렬로 생성하고, 각 프로세스에서 숫자의 제곱을 계산합니다. map 함수는 입력 리스트의 각 요소에 대해 병렬로 작업을 수행한 결과를 반환합니다.

 

 

멀티스레딩과 멀티프로세싱 비교

멀티스레딩과 멀티프로세싱은 각각의 장단점이 있습니다. 멀티스레딩은 메모리 공유가 용이하고, I/O 바운드 작업에서 효율적입니다. 반면 멀티프로세싱은 각 프로세스가 독립적인 메모리 공간을 사용하므로, CPU 바운드 작업에서 GIL의 영향을 받지 않고 더 나은 성능을 발휘할 수 있습니다. 선택은 주어진 작업의 특성과 요구에 따라 달라집니다.

 

 

 

 

실전 예제

실제 응용 프로그램에서 멀티스레딩과 멀티프로세싱을 어떻게 사용할 수 있는지 살펴보겠습니다. 웹 크롤링, 대규모 데이터 처리, 병렬 계산 등 다양한 상황에서 이들 기술을 활용할 수 있습니다. 예를 들어, 웹 크롤링에서 각 스레드가 다른 웹 페이지를 동시에 요청하는 방식으로 멀티스레딩을 사용할 수 있습니다.

import threading
import requests

def fetch_url(url):
    response = requests.get(url)
    print(f"{url}: {response.status_code}")

urls = ["https://example.com", "https://example.org", "https://example.net"]

threads = []
for url in urls:
    thread = threading.Thread(target=fetch_url, args=(url,))
    threads.append(thread)
    thread.start()

for thread in threads:
    thread.join()

위의 예제는 멀티스레딩을 사용해 동시에 여러 웹 페이지를 요청하는 간단한 웹 크롤러입니다. 각 스레드는 별도의 URL을 처리하며, 병행성 덕분에 전체 요청 시간이 단축될 수 있습니다.

 

 

주의사항과 최적화

멀티스레딩과 멀티프로세싱을 사용할 때는 교착 상태(deadlock), 경쟁 상태(race condition) 등의 문제에 주의해야 합니다. 특히, 공유 자원을 다룰 때는 적절한 락(lock)을 사용해 데이터 무결성을 유지해야 합니다. 또한, 병행성으로 인해 오히려 성능이 저하될 수 있는 경우도 있으므로, 작업의 특성에 맞는 최적화를 신중하게 적용해야 합니다.

파이썬(Python)에서 병행성을 적절히 활용하면 복잡한 작업을 보다 효율적으로 처리할 수 있습니다. 멀티스레딩과 멀티프로세싱은 각각 다른 상황에서 최적의 성능을 발휘할 수 있으며, 이를 이해하고 효과적으로 적용하는 것이 중요합니다.