39. Python에서의 스레드(비동기 프로그래밍)

안녕하세요. 언제나 휴일, 언휴예요.

이번 강의에서는 Python에서 비동기 프로그래밍에 관하여 다룰 거예요.

비동기 프로그래밍이란 서로 간섭없이 동작하는 독립된 작업을 진행하는 프로그램을 개발하는 것을 말합니다. 비동기 프로그래밍에서 독립적인 작업을 만드는 가장 간단한 방법이 스레드를 이용하는 거예요. 여기에서는 함수로 스레드를 생성하는 방법과 스레드 개체를 이용하는 방법을 다룰 거예요.

프로그램이 시작하면서 메인 스레드가 동작합니다. 따라서 우리가 만드는 프로그램은 최소 하나의 스레드가 동작한다고 말할 수 있어요. 만약 개발자가 별도의 스레드를 생성하기를 원한다면 스레드를 생성하는 함수(혹은 메서드)를 호출합니다. 이 때 인자로 생성할 스레드가 시작할 함수명을 전달하는데 이를 스레드 진입점 함수라고 부릅니다.

주의할 점은 프로그램이 동작하면서 수행하는 메인 스레드가 끝나면 프로세스가 끝납니다. 이는 메인 스레드가 끝나면 나머지 스레드(서브 스레드 혹은 자식 스레드)들도 모두 종료한다는 것입니다.

다음은 스레드가 무엇인지 보여주는 간단한 예제 코드입니다.

#스레드 개요

import _thread
import time
import random

def DoItThread(str):
    cnt = 0
    while(cnt<10):
        time.sleep(random.randint(0,100)/300.0)
        print(str,cnt)
        cnt+=1

_thread.start_new_thread(DoItThread,("홍길동",))
_thread.start_new_thread(DoItThread,("강감찬",))
print("멈추고 싶으면 아무키나 누르세요.")
input()
[그림 1] 스레드 개요 실행 화면
[그림 1] 스레드 개요 실행 화면

Python에서는 스레드를 사용하기 위해 _thread 혹은 threading 모듈을 import하여 사용합니다. 여기에서는 _thread를 import하였습니다.

스레드는 독립적으로 수행하는 작업으로 _thread의 start_new_thread 메서드를 이용하여 스레드 진입점을 전달하면 독립적인 스레드를 생성하여 전달한 스레드 진입점부터 작업을 수행합니다. 참고로 threading 모듈을 import하였을 때는 threading._start_new_thread를 호출하세요.

_thread.start_new_thread(스레드 진입점, (스레드 진입점에 전달할 인자) )

예제에서는 DoItThread 이름의 함수를 스레드 진입점으로 사용하고 있습니다.

그리고 start_new_thread 메서드에 튜플 형식으로 스레드 진입점에 전달할 인자를 전달할 수 있어요. 여기에서는 이름을 스레드 진입점 인자로 전달하고 있습니다.

위 예제에서는 DoItThread를 진입점으로 하는 두 개의 스레드를 생성하였습니다. 동작하는 것을 보면 두 개의 스레드에서 서로 간섭없이 독립적으로 수행하는 것을 알 수 있습니다.

참고로 input 함수를 호출한 이유는 메인 스레드가 끝나면 프로세스가 끝나기 때문입니다.

이번에는 threading을 import하여 스레드 개체를 만들어서 멤버 메서드를 이용하여 스레드를 시작하고 종료할 때까지 대기하는 방법을 알아볼게요.

threading을 import하였을 때 threading.Thread 메서드 호출로 스레드 개체를 생성할 수 있습니다. 이 때 스레드 진입점은 target, 스레드 진입점에 전달할 인자는 args에 튜플을 전달합니다.

th_a = threading.Thread(target = DoItThread, args=("홍길동",))

그리고 스레드를 가동할 때 스레드 개체의 start 메서드를 이용하고 스레드의 종료까지 대기할 때는 join 메서드를 호출하세요.

print("=== 스레드 가동 ===")
th_a.start()

th_a.join()
print("테스트 종료")

다음 예제 코드는 앞에서 작성한 테스트 코드를 threading을 import하여 스레드 개체를 이용하는 것으로 변경한 것입니다.

#스레드 종료 대기

import threading
import time
import random

def DoItThread(str):
    cnt = 0
    while(cnt<10):
        time.sleep(random.randint(0,100)/300.0)
        print(str,cnt)
        cnt+=1
    print("=== ",str,"스레드 종료 ===")

th_a = threading.Thread(target = DoItThread, args=("홍길동",))
th_b = threading.Thread(target = DoItThread, args=("강감찬",))

print("=== 스레드 가동 ===")
th_a.start()
th_b.start()

th_a.join()
th_b.join()
print("테스트 종료")
[그림 2] 스레드 개체 이용 실행 화면
[그림 2] 스레드 개체 이용 실행 화면