1. threading.Event() 객체의 사용
🎯 threading.Event()란?
threading.Event()는 스레드 간의 동기화를 도와주는 객체
스레드가 특정 이벤트(신호)를 기다리도록 만들고, 다른 스레드가 신호를 보내서 진행을 제어할 수 있음.
🎯 threading.Event()의 주요 기능
- event.set() → 이벤트 신호를 발생(켜기)
- event.clear() → 이벤트 신호를 리셋(끄기)
- event.wait() → 이벤트가 발생할 때까지 기다림
🎯 threading.Event()를 사용할 때의 장점
✅ 스레드 간 동기화
✅ 불필요한 CPU 사용 방지 (while 루프 대신 wait() 사용)
✅ 메인 프로세스가 특정 작업이 끝날 때까지 기다릴 수 있음
하나의 파일에서 여러 스레드를 컨트롤한다면 아래 예제로도 무리가 없겠지만, threading.Event()객체를 여러 파일에서 글로벌하게 공유해야한다고 할때는 global 방식으로 해야할까, arg로 객체를 넘겨줘야할까
import pygame
import threading
import time
# 이벤트 객체 생성
tuning_done = threading.Event()
def play_tuning_sound():
"""튜닝 소리를 재생하는 스레드"""
pygame.mixer.init()
pygame.mixer.music.load("tuning_sound.mp3")
pygame.mixer.music.play()
print("🔧 튜닝 사운드 재생 중...")
time.sleep(5) # 튜닝 사운드가 5초 동안 재생된다고 가정
pygame.mixer.music.stop()
print("✅ 튜닝 완료!")
tuning_done.set() # 이벤트 신호 발생
def start_main_audio():
"""튜닝 완료 후, 메인 오디오를 재생하는 스레드"""
print("🚀 메인 오디오 대기 중...")
tuning_done.wait() # 튜닝 완료될 때까지 대기
pygame.mixer.music.load("main_audio.mp3")
pygame.mixer.music.play()
print("🎵 메인 오디오 재생 시작!")
# 스레드 실행
tuning_thread = threading.Thread(target=play_tuning_sound)
main_audio_thread = threading.Thread(target=start_main_audio)
tuning_thread.start()
main_audio_thread.start()
tuning_thread.join()
main_audio_thread.join()
✅ 결론: global을 피하고 인자로 전달하는 것이 더 좋은 이유
❌ global 사용✅ threading.Event() 인자로 전달
audio_player.py가 main.py의 변수를 직접 참조 → 모듈 간 의존성 증가 | main.py에서 직접 tuning_done을 전달 → 독립성 유지 |
순환 참조 위험 (from main import tuning_done 하면 main.py가 먼저 실행되지 않으면 오류 발생) | 순환 참조 문제 없음 |
global 변수 변경이 어디서든 일어날 수 있어 버그 발생 가능성 증가 | tuning_done이 명확하게 어디서 전달되는지 알 수 있어 코드 유지보수 용이 |
main.py에서 변수 선언
import threading
import time
from audio.audio_player import play_tuning_audio
# ✅ `tuning_done` 선언 (전역 변수 아님)
tuning_done = threading.Event()
def main():
"""메인 실행 함수"""
print("🚀 튜닝 사운드 재생 시작!")
# ✅ `tuning_done`을 인자로 전달
tuning_thread = threading.Thread(target=play_tuning_audio, args=(tuning_done,))
tuning_thread.start()
# ✅ 5초 후 튜닝 완료
time.sleep(5)
print("✅ 튜닝 완료! 오디오 중단")
tuning_done.set() # ✅ 이벤트 발생 → 튜닝 종료 신호
tuning_thread.join()
print("🎵 모든 작업 완료!")
if __name__ == "__main__":
main()
audio/audio_player.py (인자로 전달)
import pygame
import time
import os
def get_random_tuning_audio():
return "tuning_audio.mp3"
def play_tuning_audio(tuning_done):
"""튜닝 사운드를 계속 재생하는 스레드"""
pygame.mixer.init()
count = 0
tuning_audio = get_random_tuning_audio()
while not tuning_done.is_set(): # ✅ 튜닝 완료될 때까지 반복
count += 1
if count % 3 == 0:
tuning_audio = get_random_tuning_audio()
if tuning_audio and os.path.exists(tuning_audio):
sound = pygame.mixer.Sound(tuning_audio)
sound.play(-1)
print(f"📡 튜닝 사운드 재생 중: {tuning_audio}")
time.sleep(1)
pygame.mixer.stop()
print("🎵 튜닝 사운드 중단됨")
모듈 간의 의존성을 줄이고, 유지보수가 쉬운 구조로 유지하자..
🔥 Q1: event.clear()를 하면 tuning_done 객체는 다시 사용할 수 없는 건가?
event.clear()는 메모리를 해제하는 게 아니라 이벤트 상태를 "OFF"로 변경하는 역할을 함.
즉, 다시 set()을 호출하면 언제든지 재사용 가능!
✅ 이벤트는 ON/OFF 스위치처럼 동작
- event.set() → ON (True) 상태 → wait()을 호출한 스레드가 진행됨.
- event.clear() → OFF (False) 상태 → wait()을 호출한 스레드가 다시 대기 상태로 변함.
🔥 Q2: event.set() 상태 검사 vs event.wait() 후 코드 실행 차이점?
🔹 비교event.is_set() vs event.wait()
💡 의미 | 현재 상태를 즉시 검사 (True/False) | 이벤트가 set() 될 때까지 대기 |
⏳ 블로킹 여부 | ❌ 비동기 (즉시 실행 가능) | ✅ 블로킹 (이벤트가 발생할 때까지 멈춤) |
🎯 언제 사용? | 이벤트 상태를 수시로 확인해야 할 때 | 특정 이벤트가 발생할 때까지 대기해야 할 때 |
🛠 코드 흐름 | if event.is_set(): 사용 | event.wait() 사용 |
✅ event.is_set() → 비동기적으로 상태를 확인하고 싶을 때 사용.
✅ event.wait() → 특정 시점까지 코드 실행을 멈추고 기다리고 싶을 때 사용
2. 스레드 arg 중 daemon=True/False 의 의미
🔥 데몬 스레드와 일반(Non-daemon) 스레드 차이
🔹 구분🔥 일반 스레드 (daemon=False, 기본값)🚀 데몬 스레드 (daemon=True)
실행 방식 | 메인 프로그램이 종료돼도 스레드가 계속 실행됨 | 메인 프로그램이 종료되면 자동으로 스레드 종료 |
언제 사용? | 반드시 끝까지 실행해야 하는 작업 (예: 파일 저장, 데이터베이스 업데이트) | 백그라운드에서 실행되는 작업 (예: 로그 기록, 음악 재생) |
수동 종료 필요? | thread.join()을 사용해야 안전하게 종료됨 | 메인 프로그램이 종료되면 자동 종료 |
import threading
import time
from audio.audio_player import play_tuning_audio, stop_tuning_audio
# ✅ `tuning_done`을 전역적으로 선언
tuning_done = threading.Event()
def main():
"""메인 실행 함수"""
print("🚀 튜닝 사운드 재생 시작!")
tuning_thread = threading.Thread(target=play_tuning_audio, args=(tuning_done,))
tuning_thread.start()
# ✅ 5초 후 튜닝 완료 (예제)
time.sleep(5)
print("✅ 튜닝 완료! 오디오 중단")
tuning_done.set() # ✅ 이벤트 발생 → 튜닝 종료 신호
tuning_thread.join()
print("🎵 모든 작업 완료!")
if __name__ == "__main__":
main()
'개발로그 > Python' 카테고리의 다른 글
Preload + On-demand + Fallback 구조 (캐싱전략에 관하여, 용어설명) 메모 (0) | 2025.03.12 |
---|---|
python - API 연동할 때, 인증코드를 모듈마다 넣는 이유..가 있었다.. 무식해서 그런게 아니라규..ㅠ (0) | 2025.03.11 |
구글드라이브에서 API로 파일 탐색을 할 때, 어떻게 하는게 더 효율적일까? feat ChatGPT (0) | 2025.03.11 |
Pygame에서 나오는 모든 소리를 하나의 파일(wav)로 저장하는 방법 (0) | 2025.03.10 |
FFmpeg 다운로드 및 설치 (0) | 2025.03.08 |
Google Drive에 있는 파일을 다운로드하는 세가지 방법 (0) | 2025.03.07 |
Vscode or Jupyter notebook conda 환경에서 실행되도록 설정하기 (0) | 2025.03.06 |
파이썬을 활용하는 머신러닝 딥러닝 차이 그리고 ai란? 한방에 정리하기 (2) | 2022.12.20 |
댓글