본문 바로가기
데이터&AI/기타개발

[realtime GPT를 쓰기위한 사전학습] threading의 이해 (python의 멀티태스킹!)

by 일등박사 2024. 10. 17.
728x90

 

최근 공개된 GPT realtime모델!!!

2024.10.12 - [데이터&AI/LLM] - gpt realtime console로 사용해보기

 

gpt realtime console로 사용해보기

24년 9월 25일!! chatgpt advanced voice 모드가 공개되서 많이 환영을 받았습니다!그리고 10월1일!! 이 realtime의 API가 공개되었습니다!! 이 realime에 대하여 간단히 이해해보자면!! 아래와 같았습니다!!  

drfirst.tistory.com

 

기존의 API방식과 다름에 사전 학습이 필요한데요!!

이에 앞으로 필요한 지식들을 공부해보겠습니다!!


threading 의 기본개념!!

1. THread란 ? : 실행의 단위!!!

 > 프로그램이 동시에 여러 작업을 수행할 수 있도록 도와주는 하나하나의 프로세스로 이해할수 있습니다!!

 

2. Thread가 필요한이유는!? .

 > 기존 API로 데이터를 주고받을떄는 순차적으로 진행하면 됬지만,

 > 실시간 음성으로 데이터를 주고 받기위해서는 병렬 처리가 필요합니다!!

 > 예를 들면, 아래와 같이 3가지가 필요하겠지요!?

  a. 음성데이터를 데이터로 바꾸는 Thread 

  b. GPT 서버에 데이터 보내고 받는 Thread

   c. GPT로부터 받은 데이터를 사운드데이터로 바꾸는 Thread


코드로 threading 이해하기!!

ㄱ. 간단Threading

한번 예시코드로 알아봅시다!!

import threading
import time

def print_numbers():
    for i in range(5):
        print(f"Number: {i}")
        time.sleep(1)

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

# 스레드 시작
worker1_thread.start()

 

위와같이 함수를 먼저 만들고 (print_numbers)

이 함수로 일할 worker1_thread 를 지정해줍니다 

마지막으로 해당 worker1_thread.start()를 통해 해당 쓰레드를 실행시켜주어요!!

 

그럼 위 이미지와 같이 뒷단에서 worker1_thread 가 일을 하며 print하는것을 확인할 수 있습니다~

 

ㄴ. 두개Threading

- 이번엔, 코드 두개를 함께 실행시켜볼까요!?

import threading
import time

def print_numbers():
    for i in range(5):
        print(f"Number: {i}")
        time.sleep(1)

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

# 스레드1 시작
worker1_thread.start()
# 스레드2 시작
worker2_thread.start()

위 코드를 보면 일시키는 함수(print_numbers)를 worker1과2에 배정하고 실행해줍니다!!

그럼 위와 같이 2개가 일하기에 print가 2번 찍히죠~~

 

ㄷ. Threading 과  Main 작업 함께!!

마지막으로 본 작업과 back에서 도는 threading을 함께 해보겠습니다!

 

import threading
import time

def print_numbers():
    for i in range(10):
        print(f"Number: {i}")
        time.sleep(1)

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

# 스레드 시작
worker1_thread.start()

# 메인 스레드에서도 작업을 병행 가능
for i in range(3):
    print(f"Main Worker: {i}")
    time.sleep(0.5)

 

위 코드를 본다면!! worker1_thread는 print_numbers 일을하고

main worker는 기존의 코드대로 숫자 print 를 진행하겠지요!?

 

 

네!! 맞습니다 ㅎㅎㅎ Main 과 Worker_thread가 병렬로 일을 잘하네요!

 


※ 스레드 사용 시 주의사항

병렬처리!!! 편리하고 좋겠지만 단점도 있겠지요!?
이 멀티스레딩을 사용할 때는 주의해야 할 몇 가지 사항이 있습니다:
A. 경쟁 상태(Race Condition): 여러 스레드가 동일한 자원에 접근할 때 문제가 발생할수 있습니다!! 이에 순서대로 하라고 lock 기능이 사용되지요!!

B. 데드락(Deadlock): 두 개 이상의 스레드가 서로 자원을 기다리며 무한히 멈추는 상황. 이를 피하기 위해서는 순서를 정하고, 최소한의 락만 사용하도록 야 합니다.

C.GIL(Global Interpreter Lock): Python의 기본 구현인 CPython에서는 **GIL**이라는 한계가 있어, 멀티스레딩이 CPU 바운드 작업에서는 효과적이지 않을 수 있습니다. 따라서, I/O 바운드 작업 에 멀티스레딩을 사용하는 것이 적합합니다.

ㄹ. Threading 의 추가기능 - Daemon!!

daemon이란! 백그라운드에서 실행되는 프로그램으로!!

일반적으로 우리는 nohup 혹은 systemctl 등의 방식으로 서버의 백그라운드에서 작동을 시켯습니다!

한편, 이 Thread에서도 백그라운드에서 작동하게 하는 daemon 기능이 있는데요!!

코드로는 아래와 같습니다!!

{thread객채].daemon = True  # 데몬 스레드로 설정

 

 

한번 상황을 상상해봅시다!!

우리의 Main worker가 달러를 계산합니다!

그럼 back에서 도는 daemon에서 main 이 계산한 값에 1300을 곱한  원화 값을 자동으로 산출해야하느 상황입니다!

 

즉 main worker가 달러값을 산출이 끝날때까지 back에서 살아서 원화를 계산해야하는데요,

코드로 봅시다!!

 

import threading
import time

# 글로벌 변수로 달러 값을 저장
dollar_value = 0


# 데몬 작업을 수행하는 함수 (원화 값 계산)
def daemon_worker():
    global dollar_value
    while True:
        if dollar_value != 0:
            won_value = dollar_value * 1300
            print(f"Daemon calculated: {won_value} 원")
        time.sleep(1)  # 대기하면서 값이 업데이트되길 기다림
        
# 데몬 스레드 생성
daemon_thread = threading.Thread(target=daemon_worker)
daemon_thread.setDaemon(True)  # 이 스레드를 데몬으로 설정
daemon_thread.start()


# 메인 작업 
# 여기에서 달러 값 계산 (예: 100 달러로 가정)
dollar_value_l = [100,130,160]
for d in dollar_value_l:
    dollar_value = d
    print(f"Main worker calculated: ${dollar_value}")
    time.sleep(1)  # 대기 시간 (계산이 완료되기까지의 시간)
print("Main worker completed.")

 

어때요 이해가 되시나요!?

Daemon인 daemon_thread가 global 변수인 dollar_value를 받아서 1300을 곱하는 작업을 백에서 1초마다 진행합니다!

그럼 main 코드에서 100,130,160이 각각 dollar_value가 될때마다 백에서 대몬이 그 값으 계산해 주지요!

참 쉽죠?!^^*

 

ㅁ. Threading 의 추가기능 - join

thread는 쓰레드일 뿐입니다!

무슨말이냐구요!? main 이 아니기에,, 전체 프로세스가 종료되면 그냥 꺼져벼려요!

 

예를 들어! 아래 코드를 봅시다!

import threading
import time

def producer():
    print("Producer 시작")
    time.sleep(2)
    print("Producer 종료")

def consumer():
    print("Consumer 시작")
    time.sleep(1)
    print("Consumer 종료")

producer_thread = threading.Thread(target=producer)
consumer_thread = threading.Thread(target=consumer)

# 스레드 실행
producer_thread.start()
consumer_thread.start()

# join()을 호출하지 않음
print("메인 스레드: 끝났습니다.")

 

위 코드에 메인 작업은 

print("메인 스레드: 끝났습니다.")

가 전부입니다!! 그렇기에 producer_thread와 consumer_thread 는 시작과 동시에 종료되지요!

 

위 코드를 실행해볼까요!?

시작과 동시에 sleep 1의 종료를 못찍고 끝나버립니다!!

왜냐!? main 이 끝났으니까요!!

이를 방지하기위해서 join이라는 기능이 도입되었습니다!

아래 1줄의 코드만 추가해줍니다!

producer_thread.join()  # producer_thread가 끝날 때까지 대기

 

전체코드는!!

import threading
import time

def producer():
    print("Producer 시작")
    time.sleep(2)
    print("Producer 종료")

def consumer():
    print("Consumer 시작")
    time.sleep(1)
    print("Consumer 종료")

producer_thread = threading.Thread(target=producer)
consumer_thread = threading.Thread(target=consumer)

# 스레드 실행
producer_thread.start()
# producer_thread가 끝날 때까지 대기 <<<< 여기가 추가됨!
producer_thread.join()

consumer_thread.start()

# join()을 호출하지 않음
print("메인 스레드: 끝났습니다.")

 

이 코드를 돌린다면!?

위와같이 메인이 thread와 연결되어서 끝나기를 기다리기에!!

종료를 볼수 있지요!!^^

 

이제 realtime gpt 를 이해하기위한 한발자국 완성!!

728x90

댓글