프로그래밍/Python

[Python 강의] 17강 - 병렬 처리 및 멀티스레딩

월횽 2024. 10. 22. 06:30
728x90
반응형
SMALL

안녕하세요! 그레이 해커 월횽입니다. 오늘은 Python에서 병렬 처리 및 멀티스레딩에 대해 알아보겠습니다. Python은 GIL(Global Interpreter Lock) 때문에 기본적으로 한 번에 하나의 스레드만 실행할 수 있습니다. 하지만 이를 극복하고 CPU의 여러 코어를 활용하여 성능을 높이기 위해 병렬 처리와 멀티스레딩을 사용할 수 있습니다. 이번 강의에서는 멀티스레딩, 멀티프로세싱 및 비동기 처리에 대해 다루겠습니다.

 

 

1. 병렬 처리와 멀티스레딩의 기본 개념

· 병렬 처리: 여러 작업을 동시에 실행하여 성능을 극대화하는 기법입니다. 보통 여러 CPU 코어를 활용하는 경우에 사용됩니다.
· 멀티스레딩: 하나의 프로세스 내에서 여러 스레드를 사용하여 작업을 동시에 실행하는 방식입니다. I/O 작업(파일 읽기/쓰기, 네트워크 통신)에서는 유용하지만, GIL로 인해 CPU 바운드 작업에서는 성능 개선이 제한적입니다.

 

 

2. threading 모듈을 사용한 멀티스레딩

Python에서 threading 모듈을 사용하면 간단하게 멀티스레딩을 구현할 수 있습니다. 주로 I/O 작업에서 유리합니다.

 

threading 모듈을 사용한 기본 스레드 예시

import threading
import time

# 스레드에서 실행할 함수 정의
def print_numbers():
    for i in range(5):
        print(f"Number: {i}")
        time.sleep(1)

# 스레드 생성
thread = threading.Thread(target=print_numbers)

# 스레드 시작
thread.start()

# 메인 스레드에서 다른 작업 수행
print("메인 스레드에서 작업 중...")

# 스레드가 종료될 때까지 기다림
thread.join()
print("스레드 종료")

 

반응형

스레드 간 공유 자원 관리 (Lock 사용)

멀티스레딩 환경에서는 동시성 문제가 발생할 수 있으므로, Lock을 사용하여 공유 자원을 보호해야 합니다.

import threading

# 공유 변수
counter = 0
lock = threading.Lock()

# 스레드에서 실행할 함수
def increment_counter():
    global counter
    for _ in range(100000):
        with lock:  # Lock을 사용해 동시성 문제 방지
            counter += 1

# 두 개의 스레드 생성
thread1 = threading.Thread(target=increment_counter)
thread2 = threading.Thread(target=increment_counter)

# 스레드 시작
thread1.start()
thread2.start()

# 두 스레드가 종료될 때까지 기다림
thread1.join()
thread2.join()

# 결과 출력
print(f"최종 카운터 값: {counter}")

 

 

 

3. multiprocessing 모듈을 사용한 병렬 처리

multiprocessing 모듈은 멀티프로세싱을 지원하여 GIL의 영향을 받지 않고 여러 프로세스를 생성하여 병렬로 작업을 처리할 수 있습니다.

 

multiprocessing 모듈을 사용한 병렬 처리 예시

import multiprocessing

# 프로세스에서 실행할 함수
def square_number(n):
    return n * n

if __name__ == "__main__":
    # 여러 프로세스에서 처리할 데이터
    numbers = [1, 2, 3, 4, 5]

    # 병렬 처리 풀 생성
    with multiprocessing.Pool() as pool:
        # 병렬로 작업 실행
        results = pool.map(square_number, numbers)

    print(results)  # [1, 4, 9, 16, 25] 출력

 

728x90

multiprocessing.Queue를 사용한 프로세스 간 통신
멀티프로세스 간 통신을 위해 Queue를 사용할 수 있습니다.

import multiprocessing

# 자식 프로세스에서 실행할 함수
def worker(queue):
    queue.put("작업 완료!")

if __name__ == "__main__":
    # Queue 생성
    queue = multiprocessing.Queue()

    # 프로세스 생성
    process = multiprocessing.Process(target=worker, args=(queue,))

    # 프로세스 시작
    process.start()

    # 자식 프로세스의 결과 대기
    print(queue.get())

    # 프로세스 종료
    process.join()

 

 

 

4. asyncio 모듈을 사용한 비동기 처리

asyncio는 비동기 방식으로 동시성을 처리할 수 있는 모듈입니다. 특히 I/O 바운드 작업에 적합하며, 멀티스레딩이나 멀티프로세싱에 비해 가볍고 효율적입니다.

SMALL

asyncio로 비동기 함수 실행 예시

import asyncio

# 비동기 함수 정의
async def print_delayed(message, delay):
    await asyncio.sleep(delay)
    print(message)

# 메인 함수 정의
async def main():
    task1 = asyncio.create_task(print_delayed("첫 번째 메시지", 2))
    task2 = asyncio.create_task(print_delayed("두 번째 메시지", 1))

    await task1
    await task2

# 이벤트 루프 실행
asyncio.run(main())

 

 

 

5. 멀티스레딩, 멀티프로세싱, 비동기 처리 비교

· 멀티스레딩: I/O 바운드 작업에 적합하지만 GIL로 인해 CPU 바운드 작업에는 성능 제한이 있습니다.
· 멀티프로세싱: GIL의 영향을 받지 않고 CPU 코어를 모두 활용하여 병렬 처리 가능.
· 비동기 처리 (asyncio): 가벼운 동시성 처리로 I/O 바운드 작업에서 매우 효과적입니다.

 

6. 병렬 처리 및 멀티스레딩 정리

· threading 모듈을 통해 간단한 멀티스레딩을 구현할 수 있습니다.
· multiprocessing 모듈을 통해 GIL을 우회하고 병렬 처리를 구현할 수 있습니다.
· asyncio를 사용하여 비동기 방식으로 효율적인 동시성 처리를 구현할 수 있습니다.

 

다음 시간에는 Python을 활용한 네트워킹 프로그래밍에 대해 알아보겠습니다!

 

728x90
반응형
LIST