산업 제조
산업용 사물 인터넷 | 산업자재 | 장비 유지 보수 및 수리 | 산업 프로그래밍 |
home  MfgRobots >> 산업 제조 >  >> Industrial programming >> python

파이썬 멀티스레딩 마스터: GIL 완전 정복과 실전 예제

파이썬은 워크로드 가속을 위해 멀티프로세싱과 멀티스레딩을 모두 제공합니다. 이 가이드는 견고한 스레드 기반 애플리케이션 생성, 전역 인터프리터 락(GIL)의 뉘앙스, 데드락과 레이스 컨디션 같은 흔한 함정 회피법을 안내합니다.

스레드란 무엇인가?

스레드는 단일 프로세스 내의 가벼운 실행 단위입니다. 스레드는 프로세스의 메모리 공간을 공유하여 효율적인 데이터 교환을 가능케 하며, 운영체제가 하나 이상의 CPU 코어에서 동시 실행을 위해 스케줄링합니다.

프로세스란 무엇인가?

프로세스는 독립적인 프로그램 인스턴스입니다. 브라우저나 텍스트 에디터 같은 애플리케이션을 실행하면 OS는 자체 코드, 데이터, 메모리 맵을 가진 별도 프로세스를 생성합니다.

파이썬 멀티스레딩 기초

파이썬의 threading 모듈은 스레드 관리를 위한 고수준 API를 제공하며, 레거시 _thread 모듈은 하위 호환성용으로만 유지됩니다. 스레드는 I/O 바운드 작업에 적합하며, CPU 바운드 작업은 multiprocessing 모듈을 사용해 GIL을 우회해야 합니다.

멀티스레딩을 언제 사용할까?

네트워크 요청, 파일 I/O, 데이터베이스 호출 등 여러 I/O 집약적 작업을 동시에 수행해야 할 때 멀티스레딩이 빛을 발합니다. 잘 설계된 스레드는 새로운 프로세스 생성 오버헤드 없이 응답성과 처리량을 향상시킵니다.

스레딩을 위한 주요 파이썬 모듈

  1. _thread – 저수준, 사용 중단
  2. threading – 현대적이고 기능 풍부하며 권장되는 선택

threading 모듈 사용

threading 모듈은 Thread 클래스, 동기화 프리미티브(Lock, RLock, Semaphore, Condition, Event, Barrier), activeCount(), enumerate() 같은 유틸리티 함수를 제공합니다. 아래는 스레드 생성과 동기화를 보여주는 최소 예제입니다.

import time
import _thread

def thread_test(name, wait):
    for i in range(4):
        time.sleep(wait)
        print(f"Running {name}")
    print(f"{name} has finished execution")

if __name__ == "__main__":
    _thread.start_new_thread(thread_test, ("First Thread", 1))
    _thread.start_new_thread(thread_test, ("Second Thread", 2))
    _thread.start_new_thread(thread_test, ("Third Thread", 3))

이 코드를 실행하면 병렬 실행을 보여주는 인터리빙된 출력이 나옵니다.

파이썬 멀티스레딩 마스터: GIL 완전 정복과 실전 예제

설명

  1. time과 레거시 _thread 모듈 임포트
  2. thread_test 정의: 작업 수행 및 진행상황 로깅
  3. start_new_thread로 세 개의 독립 스레드 실행

현대적 스레딩: threading.Thread

실제로는 threading.Thread를 서브클래싱하고 run() 메서드를 오버라이드합니다. 다음 예제는 이전 동작을 고수준 API로 구현합니다.

import time
import threading

class ThreadTester(threading.Thread):
    def __init__(self, name, delay):
        super().__init__()
        self.name = name
        self.delay = delay

    def run(self):
        for _ in range(4):
            time.sleep(self.delay)
            print(f"Running {self.name}")
        print(f"{self.name} has finished execution")

if __name__ == "__main__":
    threads = [
        ThreadTester("First Thread", 1),
        ThreadTester("Second Thread", 2),
        ThreadTester("Third Thread", 3),
    ]
    for t in threads:
        t.start()
    for t in threads:
        t.join()

출력:

파이썬 멀티스레딩 마스터: GIL 완전 정복과 실전 예제

설명

  1. Thread 서브클래싱으로 스레드 행동 캡슐화
  2. __init__ 오버라이드로 스레드별 데이터 저장
  3. run 오버라이드로 작업 정의
  4. 인스턴스화, 시작, 조인으로 각 스레드 관리

흔한 스레딩 함정

데드락

데드락은 두 개 이상의 스레드가 서로가 가진 자원을 무기한 기다릴 때 발생합니다. 고전적 예시: 식사하는 철학자 문제 – 다섯 철학자가 다섯 포크를 두고 경쟁하며, 각자 두 포크가 있어야 식사 가능. 모두 오른쪽 포크를 먼저 집으면 순환 대기가 발생해 모든 진행이 중단됩니다.

레이스 컨디션

여러 스레드가 적절한 조정 없이 공유 데이터를 수정하면 최종 상태가 예측 불가능해집니다. 예:

i = 0
for _ in range(100):
    print(i)
    i += 1

이 루프를 병렬 스레드에서 실행하면 증감이 겹쳐 뒤섞인 시퀀스가 생성될 수 있습니다.

스레드 동기화

threading 모듈은 여러 동기화 프리미티브를 제공합니다. 가장 기본은 Lock으로, 공유 자원에 대한 독점적 접근을 보장합니다.

import threading
lock = threading.Lock()

def worker(name):
    for _ in range(5):
        lock.acquire()
        print(f"{name} acquired lock")
        # 임계 영역
        lock.release()

if __name__ == "__main__":
    t1 = threading.Thread(target=worker, args=("Thread A",))
    t2 = threading.Thread(target=worker, args=("Thread B",))
    t1.start(); t2.start()
    t1.join(); t2.join()

출력은 임계 영역에 대한 직렬화된 접근을 보여줍니다.

파이썬 멀티스레딩 마스터: GIL 완전 정복과 실전 예제

기타 동기화 도구

전역 인터프리터 락(GIL) 이해하기

CPython은 GIL을 사용해 스레드 간 바이트코드 실행을 직렬화합니다. 이는 인터프리터 내부 구조(참조 카운팅 등)를 레이스 컨디션으로부터 보호하지만, 멀티코어 시스템에서 CPU 바운드 작업의 병렬성을 제한하기도 합니다.

  1. 스레드 시작 시 GIL 획득
  2. 다른 스레드가 GIL 필요 시 첫 스레드가 해제할 때까지 대기
  3. I/O 바운드 스레드는 블로킹 호출 중 GIL 해제 → 다른 스레드 실행 가능
  4. CPU 바운드 스레드는 GIL 보유 → 다른 스레드가 파이썬 코드 실행 불가

결과적으로 파이썬 멀티스레딩은 연산 집약적 워크로드에서 이득이 적으며, 그런 경우 multiprocessing이 권장됩니다.

GIL이 존재하는 이유

CPython 가비지 컬렉터는 참조 카운팅에 의존합니다. 전역 락 없이 참조 카운트 동시 업데이트 시 레이스 컨디션과 메모리 손상 발생. 객체별 락은 큰 오버헤드 초래하므로 GIL이 단순하고 효과적인 해결책으로 채택되었습니다.

실전 핵심 포인트

python

  1. C언어 free() 함수 마스터하기: 실전 가이드와 예제
  2. Python strip() 메서드 완전 정복: 종합 가이드와 실전 예제
  3. Python str.count() 마스터하기: 문자 및 부분문자열 개수 세기 예제
  4. 파이썬 round() 함수 완전 정복: 실용 예제로 이해하기
  5. 파이썬 map() 함수 마스터하기: 문법, 예제, 모범 사례
  6. 파이썬 timeit()으로 실행 시간 측정하기: 실용 예제
  7. 파이썬 collections.Counter: 효율적 카운팅, 업데이트, 산술 연산
  8. 파이썬 list.count() 전문가 가이드: 실전 예제로 배우는 요소 개수 세기
  9. 파이썬 list.index() – 요소 위치 찾기 실전 예제와 대안 기법
  10. C# 멀티스레딩 마스터하기: 성능과 효율성 향상