스레드 타임아웃 및 데몬 스레드
이 마지막 단계에서는 스레드 관리의 두 가지 중요한 개념인 타임아웃 설정 및 데몬 스레드 사용에 대해 배우게 됩니다. 이러한 기술을 통해 스레드가 메인 프로그램과 상호 작용하는 방식을 더 잘 제어할 수 있습니다.
스레드 타임아웃 사용하기
Step 2 에서 배운 것처럼 join() 메서드는 타임아웃 매개변수를 허용합니다. 이는 스레드가 완료될 때까지 기다리되 특정 시점까지만 기다리려는 경우에 유용합니다.
타임아웃으로 데이터를 가져오려고 시도하는 함수를 구현하는 더 실용적인 예제를 만들어 보겠습니다.
-
/home/labex/project 디렉토리에 thread_with_timeout.py라는 새 파일을 생성합니다.
-
다음 코드를 추가합니다.
import threading
import time
import random
def fetch_data(data_id):
"""Simulate fetching data that might take varying amounts of time."""
print(f"Fetching data #{data_id}...")
## Simulate different fetch times, occasionally very long
fetch_time = random.choices([1, 8], weights=[0.8, 0.2])[0]
time.sleep(fetch_time)
if fetch_time > 5: ## Simulate a slow fetch
print(f"Data #{data_id}: Fetch took too long!")
return None
else:
print(f"Data #{data_id}: Fetch completed in {fetch_time} seconds!")
return f"Data content for #{data_id}"
def fetch_with_timeout(data_id, timeout=3):
"""Fetch data with a timeout."""
result = [None] ## Using a list to store result from the thread
def target_func():
result[0] = fetch_data(data_id)
## Create and start the thread
thread = threading.Thread(target=target_func)
thread.start()
## Wait for the thread with a timeout
thread.join(timeout=timeout)
if thread.is_alive():
print(f"Data #{data_id}: Fetch timed out after {timeout} seconds!")
return "TIMEOUT"
else:
return result[0]
## Try to fetch several pieces of data
for i in range(1, 6):
print(f"\nAttempting to fetch data #{i}")
result = fetch_with_timeout(i, timeout=3)
if result == "TIMEOUT":
print(f"Main thread: Fetch for data #{i} timed out, moving on...")
elif result is None:
print(f"Main thread: Fetch for data #{i} completed but returned no data.")
else:
print(f"Main thread: Successfully fetched: {result}")
print("\nAll fetch attempts completed!")
- 파일을 저장하고 실행합니다.
python3 /home/labex/project/thread_with_timeout.py
출력은 다를 수 있지만 다음과 유사해야 합니다.
Attempting to fetch data #1
Fetching data #1...
Data #1: Fetch completed in 1 seconds!
Main thread: Successfully fetched: Data content for #1
Attempting to fetch data #2
Fetching data #2...
Data #2: Fetch completed in 1 seconds!
Main thread: Successfully fetched: Data content for #2
Attempting to fetch data #3
Fetching data #3...
Data #3: Fetch timed out after 3 seconds!
Main thread: Fetch for data #3 timed out, moving on...
Data #3: Fetch took too long!
Attempting to fetch data #4
Fetching data #4...
Data #4: Fetch completed in 1 seconds!
Main thread: Successfully fetched: Data content for #4
Attempting to fetch data #5
Fetching data #5...
Data #5: Fetch completed in 1 seconds!
Main thread: Successfully fetched: Data content for #5
All fetch attempts completed!
이 예제는 다음을 보여줍니다.
- 데이터를 가져오려고 시도하고 느릴 수 있는 함수
- 타임아웃을 사용하여 스레딩을 사용하는 래퍼 함수
- 타임아웃을 적절하게 처리하고 다른 작업을 계속하는 방법
데몬 스레드 이해하기
Python 에서 데몬 스레드는 백그라운드에서 실행되는 스레드입니다. 데몬 스레드와 비데몬 스레드의 주요 차이점은 Python 이 종료되기 전에 데몬 스레드가 완료될 때까지 기다리지 않는다는 것입니다. 이는 프로그램 종료를 방해해서는 안 되는 백그라운드 작업을 수행하는 스레드에 유용합니다.
데몬 스레드를 보여주는 예제를 만들어 보겠습니다.
-
/home/labex/project 디렉토리에 daemon_threads.py라는 새 파일을 생성합니다.
-
다음 코드를 추가합니다.
import threading
import time
def background_task(name, interval):
"""A task that runs in the background at regular intervals."""
count = 0
while True:
count += 1
print(f"{name}: Iteration {count} at {time.strftime('%H:%M:%S')}")
time.sleep(interval)
def main_task():
"""The main task that runs for a set amount of time."""
print("Main task: Starting...")
time.sleep(5)
print("Main task: Completed!")
## Create two background threads
print("Creating background monitoring threads...")
monitor1 = threading.Thread(target=background_task, args=("Monitor-1", 1), daemon=True)
monitor2 = threading.Thread(target=background_task, args=("Monitor-2", 2), daemon=True)
## Start the background threads
monitor1.start()
monitor2.start()
print("Background monitors started, now starting main task...")
## Run the main task
main_task()
print("Main task completed, program will exit without waiting for daemon threads.")
print("Daemon threads will be terminated when the program exits.")
- 파일을 저장하고 실행합니다.
python3 /home/labex/project/daemon_threads.py
출력은 다음과 유사해야 합니다.
Creating background monitoring threads...
Background monitors started, now starting main task...
Main task: Starting...
Monitor-1: Iteration 1 at 14:25:10
Monitor-2: Iteration 1 at 14:25:10
Monitor-1: Iteration 2 at 14:25:11
Monitor-1: Iteration 3 at 14:25:12
Monitor-2: Iteration 2 at 14:25:12
Monitor-1: Iteration 4 at 14:25:13
Monitor-1: Iteration 5 at 14:25:14
Monitor-2: Iteration 3 at 14:25:14
Main task: Completed!
Main task completed, program will exit without waiting for daemon threads.
Daemon threads will be terminated when the program exits.
이 예제에서:
- 정기적으로 메시지를 인쇄하면서 지속적으로 실행되는 두 개의 데몬 스레드를 생성했습니다.
- 스레드를 생성할 때
daemon=True를 설정하여 데몬 스레드로 표시했습니다.
- 메인 스레드는 5 초 동안 실행된 다음 종료됩니다.
- 메인 스레드가 종료되면 프로그램이 종료되고 데몬 스레드도 자동으로 종료됩니다.
비데몬 스레드 vs. 데몬 스레드
차이점을 더 잘 이해하기 위해 데몬 스레드와 비데몬 스레드를 비교하는 예제를 하나 더 만들어 보겠습니다.
-
/home/labex/project 디렉토리에 daemon_comparison.py라는 새 파일을 생성합니다.
-
다음 코드를 추가합니다.
import threading
import time
def task(name, seconds, daemon=False):
"""A task that runs for a specified amount of time."""
print(f"{name} starting {'(daemon)' if daemon else '(non-daemon)'}")
time.sleep(seconds)
print(f"{name} finished after {seconds} seconds")
## Create a non-daemon thread that runs for 8 seconds
non_daemon_thread = threading.Thread(
target=task,
args=("Non-daemon thread", 8, False),
daemon=False ## This is the default, so it's not actually needed
)
## Create a daemon thread that runs for 8 seconds
daemon_thread = threading.Thread(
target=task,
args=("Daemon thread", 8, True),
daemon=True
)
## Start both threads
non_daemon_thread.start()
daemon_thread.start()
## Let the main thread run for 3 seconds
print("Main thread will run for 3 seconds...")
time.sleep(3)
## Check which threads are still running
print("\nAfter 3 seconds:")
print(f"Daemon thread is alive: {daemon_thread.is_alive()}")
print(f"Non-daemon thread is alive: {non_daemon_thread.is_alive()}")
print("\nMain thread is finishing. Here's what will happen:")
print("1. The program will wait for all non-daemon threads to complete")
print("2. Daemon threads will be terminated when the program exits")
print("\nWaiting for non-daemon threads to finish...")
## We don't need to join the non-daemon thread, Python will wait for it
## But we'll explicitly join it for clarity
non_daemon_thread.join()
print("All non-daemon threads have finished, program will exit now.")
- 파일을 저장하고 실행합니다.
python3 /home/labex/project/daemon_comparison.py
출력은 다음과 같아야 합니다.
Non-daemon thread starting (non-daemon)
Daemon thread starting (daemon)
Main thread will run for 3 seconds...
After 3 seconds:
Daemon thread is alive: True
Non-daemon thread is alive: True
Main thread is finishing. Here's what will happen:
1. The program will wait for all non-daemon threads to complete
2. Daemon threads will be terminated when the program exits
Waiting for non-daemon threads to finish...
Non-daemon thread finished after 8 seconds
All non-daemon threads have finished, program will exit now.
주요 관찰 사항:
- 두 스레드가 모두 시작되어 동시에 실행됩니다.
- 3 초 후 두 스레드가 모두 계속 실행됩니다.
- 프로그램은 비데몬 스레드가 완료될 때까지 기다립니다 (8 초 후).
- 프로그램이 종료될 때 데몬 스레드는 여전히 실행 중이지만 종료됩니다.
- 데몬 스레드는 프로그램이 종료될 때 종료되므로 완료 메시지를 인쇄하지 않습니다.
데몬 스레드를 사용해야 하는 경우
데몬 스레드는 다음과 같은 경우에 유용합니다.
- 백그라운드 모니터링 작업
- 정리 작업
- 프로그램 실행 시간 동안 실행되어야 하지만 종료를 방해하지 않는 서비스
- 정기적으로 이벤트를 트리거하는 타이머 스레드
비데몬 스레드는 다음과 같은 경우에 적합합니다.
- 완료되어야 하는 중요한 작업
- 중단되어서는 안 되는 작업
- 프로그램이 종료되기 전에 깔끔하게 완료되어야 하는 작업
각 유형을 언제 사용해야 하는지 이해하는 것은 강력한 멀티 스레드 애플리케이션을 설계하는 데 중요한 부분입니다.