CodeGym /행동 /Python SELF KO /threading 모듈

threading 모듈

Python SELF KO
레벨 25 , 레슨 1
사용 가능

2.1 threading 모듈

Python에서의 멀티스레딩은 여러 스레드 (threads)를 동시에 실행할 수 있는 방법이며, 특히 입출력 작업이나 병렬로 수행할 수 있는 작업에서 CPU 자원을 더욱 효율적으로 활용할 수 있게 해줘.

Python에서 멀티스레딩의 기본 개념:

스레드란 실행의 최소 단위로, 하나의 프로세스 내에서 다른 스레드와 병렬로 작동할 수 있어. 모든 스레드는 하나의 프로세스 내에 있어서 메모리를 공유하며, 서로 데이터를 교환할 수 있지.

프로세스란 운영 체제에서 고유한 주소 공간과 자원을 가지고 실행되는 프로그램의 인스턴스야. 스레드와 다르게, 프로세스끼리는 서로 격리되어 있고, 데이터는 IPC(Inter-Process Communication)를 통해 교환해.

GIL은 Python 인터프리터의 메커니즘으로, 여러 Python 스레드가 동시에 실행되는 것을 막아. GIL은 Python 코드의 안전한 실행을 보장하지만, 멀티코어 프로세서에서 멀티스레드 프로그램의 성능을 제한해.

중요! Global Interpreter Lock (GIL) 때문에, 멀티스레딩이 계산 집중형 작업에 대해 성능 향상을 크게 보장하지 못할 수도 있는 점을 고려해야 해. GIL이 멀티코어 프로세서에서 여러 Python 스레드의 동시 실행을 막기 때문이야.

threading 모듈

Python의 threading 모듈은 스레드와 작업하기 위한 고급 인터페이스를 제공해. 스레드를 생성하고 관리하며, 이들을 동기화하고 상호작용을 조직할 수 있게 해줘. 이 모듈의 주요 구성 요소와 기능을 좀 더 자세히 살펴보자.

threading 모듈의 주요 구성 요소

스레드 작업을 위한 엔티티:

  • Thread — 스레드 생성 및 관리의 기본 클래스.
  • Timer — 지정된 시간이 지나면 함수를 실행하는 타이머.
  • ThreadLocal — 스레드에 로컬 데이터를 생성할 수 있게 해.

스레드 동기화 메커니즘:

  • Lock — 공용 자원에 대한 동시 접근을 방지하기 위한 동기화 프리미티브.
  • Condition — 보다 복잡한 스레드 동기화를 위한 조건 변수.
  • Event — 스레드 간의 알림을 위한 프리미티브.
  • Semaphore — 특정 구역을 동시에 실행할 수 있는 스레드 수를 제한하는 프리미티브.
  • Barrier — 설정된 수의 스레드를 동기화하여, 모든 스레드가 도달할 때까지 차단함.

아래에서 스레드와 작업할 수 있는 3가지 클래스를 설명할 거야. 동기화 메커니즘은 당분간 필요 없을 거야.

2.2 Thread 클래스

Thread 클래스는 스레드 생성 및 관리의 기본 클래스야. 이 클래스에는 4가지 주요 메서드가 있어:

  • start(): 스레드 실행을 시작해.
  • join(): 현재 스레드가 대기하고, 실행된 스레드가 완료될 때까지 기다려.
  • is_alive(): 스레드가 실행 중이라면 True를 반환해.
  • run(): 스레드에서 실행할 코드를 포함한 메서드. Thread 클래스를 상속받아 재정의할 수 있어.

훨씬 더 간단해 보이는 Thread 클래스의 사용 예제.

간단한 스레드 시작


import threading

def worker():
    print("Worker thread is running")
            
# 새 스레드 생성
t = threading.Thread(target=worker) #새 스레드 객체를 생성했어
t.start() # 스레드 시작
t.join() # 스레드 완료 대기
print("Main thread is finished")
        

start 메서드를 호출한 후, 함수 worker가 실행될 거야. 좀 더 정확히 말하면, 스레드가 활성 스레드 목록에 추가될 거야.

인수 사용


import threading

def worker(number, text):
    print(f"Worker {number}: {text}")
            
# 인수가 있는 새 스레드 생성
t = threading.Thread(target=worker, args=(1, "Hello"))
t.start()
t.join()
        

새 스레드에 매개변수를 전달하려면, 단순히 튜플 형태로 args 매개변수에 지정해 줄 필요가 있어. target에 지정된 함수가 호출될 때, 매개변수는 자동으로 전달될 거야.

메서드 run 재정의


import threading

class MyThread(threading.Thread):
    def run(self):
        print("Custom thread is running")
            
# 스레드 생성 및 시작
t = MyThread()
t.start()
t.join()
        

새로운 스레드를 시작할 함수는 두 가지 방법으로 지정할 수 있어 — Thread 객체를 생성할 때 target 매개변수를 통해 전달하거나, Thread 클래스를 상속받아 run 함수를 재정의하는 거야. 두 가지 방법 모두 합법적이고 자주 사용돼.

2.3 Timer 클래스

threading 모듈의 Timer 클래스는 지정된 시간 후에 함수를 실행하기 위한 목적이야. 이 클래스는 멀티스레딩 환경에서 지연된 작업을 실행하는 데 유용하지.

타이머는 호출할 함수와 초 단위의 지연 시간으로 생성 및 초기화돼.

  • 메서드 start()는 타이머를 시작하고, 지정된 시간이 지나면 주어진 함수를 호출해.
  • 메서드 cancel()은 타이머가 아직 실행되지 않았을 때 타이머를 멈출 수 있어. 타이머가 더 이상 필요하지 않은 경우에 함수를 실행하지 않도록 방지하는데 유용하지.

사용 예제:

지연 실행 함수

이 예제에서는 타이머가 시작된 후 5초 뒤에 hello 함수가 호출되어.


import threading

def hello():
    print("Hello, world!")
            
# 5초 뒤에 hello 함수를 호출할 타이머 생성
t = threading.Timer(5.0, hello)
t.start()  # 타이머 시작
        

실행 전에 타이머 멈추기

여기서는 hello 함수가 실행되기 전에 타이머가 멈추기 때문에 아무것도 출력되지 않아.


import threading

def hello():
    print("Hello, world!")
            
# 타이머 생성
t = threading.Timer(5.0, hello)
t.start()  # 타이머 시작
            
# 실행 전에 타이머 멈추기
t.cancel()
        

인수 있는 타이머

이 예제에서는 타이머가 3초 후에 greet 함수를 호출하고 인수 "Alice"를 전달해.


import threading

def greet(name):
    print(f"Hello, {name}!")
            
# 인수 있는 타이머 생성
t = threading.Timer(3.0, greet, args=["Alice"])
t.start()
        

Timer 클래스는 일정 시간 후에 작업을 계획하는 데 편리해. 다만 타이머는 작업 실행의 절대적인 정확성을 보장하지는 않음, 시스템 부하와 스레드 스케줄러 작동에 따라 다르지.

2.4 ThreadLocal 클래스

ThreadLocal 클래스는 각 스레드마다 고유한 로컬 데이터를 가지도록 하기 위한 클래스로, 멀티스레드 애플리케이션에서 각 스레드가 충돌 없이 자신의 데이터를 가져야 할 때 유용해.

ThreadLocal을 사용하는 모든 스레드에는 자신만의 고유한 데이터 사본이 있어. ThreadLocal 객체에 저장된 데이터는 각 스레드마다 독립적이고 다른 스레드와 공유되지 않아. 현재 웹 애플리케이션의 사용자나 데이터베이스와의 현재 연결 등 특정 스레드 내에서만 사용하는 데이터를 저장하기에 좋지.

사용 예제:

기본 사용

이 예제에서는 각 스레드가 로컬 변수 value에 자신의 이름을 할당하고 출력해. value의 값은 각 스레드마다 유일해.


import threading

# ThreadLocal 객체 생성
local_data = threading.local()
            
def process_data():
    # 스레드 로컬 변수에 값 할당
    local_data.value = threading.current_thread().name
    # 스레드 로컬 변수에 접근
    print(f'Value in {threading.current_thread().name}: {local_data.value}')

threads = []
for i in range(5):
    t = threading.Thread(target=process_data)
    threads.append(t)
    t.start()

for t in threads:
    t.join()

웹 애플리케이션에서 사용자 데이터 저장

이 예제에서는 각 스레드가 사용자 요청을 처리해. user_data.user 값은 각 스레드마다 유일해.


import threading
# ThreadLocal 객체 생성
user_data = threading.local()
def process_request(user):
    # 스레드 로컬 변수에 값 할당
    user_data.user = user
    handle_request()

def handle_request():
    # 스레드 로컬 변수에 접근
    print(f'Handling request for user: {user_data.user}')

threads = []
users = ['Alice', 'Bob', 'Charlie']
for user in users:
    t = threading.Thread(target=process_request, args=(user,))
    threads.append(t)
    t.start()

for t in threads:
    t.join()

이게 threading 라이브러리에서 가장 유용한 3가지 클래스였어. 아마도 이 클래스를 사용해서 작업할 가능성이 크지만, 나머지 클래스는 아마도 거의 사용할 일이 없을 거야. 요즘 모두가 비동기 함수와 asyncio 라이브러리로 전환하고 있어. 앞으로는 이 asyncio 라이브러리에 대해 계속 이야기할 거야.

코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION