최근 대용량 데이터를 처리할 일이 많아지면서 한가지 난관에 부딪혔습니다. 데이터의 양이 늘어날 수록 처리하는 시간이 길어지고 그만큼 코드에서 예상치 못한 예외가 발생하면 그만큼의 시간을 허비하는 일이 많아진 것입니다. 이걸 어떻게 해결할까 고민하던 중에 평소 즐겨하던 SNS인 미투데이의 지인들에게 물어보게 되었습니다.
학과 졸업 선배이신 맹생형이 멀티 스레드를 추천해주셨고 진행중인 프로젝트에 적용하기로 했습니다(지금은 1.7GB가 아니라 10GB가 넘는 XML데이터들을 처리하고 있습니다.).
멀티스레딩(Multithreading) 컴퓨터는 여러 개의 스레드를 효과적으로 실행할 수 있는 하드웨어 지원을 갖추고 있다. 이는 스레드가 모두 같은 주소 공간에서 동작하여 하나의 CPU 캐시 공유 집합과 하나의 변환 색인 버퍼 (TLB)만 있는 멀티프로세서 시스템 (멀티 코어 시스템)과는 구별한다. 그러므로 멀티스레딩은 프로그램 안에서 병렬 처리의 이점을 맛볼 수 있지만 멀티프로세싱 시스템은 여러 개의 프로그램들을 병렬로 처리할 수 있다. 멀티프로세싱 시스템이 여러 개의 완전한 처리 장치들을 포함하는 반면 멀티스레딩은 스레드 수준뿐 아니라 명령어 수준의 병렬 처리에까지 신경을 쓰면서 하나의 코어에 대한 이용성을 증가하는 것에 초점을 두고 있다. [출처 : 위키피디아(http://ko.wikipedia.org/wiki/%EB%A9%80%ED%8B%B0%EC%8A%A4%EB%A0%88%EB%94%A9)]
Python에서는 정말 사용하기 쉬운 멀티 스레딩 API를 지원해줍니다. Threading이 그것인데요. 자세한 API는 http://docs.python.org/library/threading.html 에서 보실 수 있습니다. Threading을 사용하려면 우선 Threading 라이브러리부터 import시켜줘야 합니다.
import threading
그리고 threading.Thread를 상속 받는 클래스를 구현합니다.
class DKThread(threading.Thread):
상속 받은 클래스에는 반드시 run()이란 메서드가 구현되 있어야 합니다. 클래스의 생성자 메서드로는 __init__()메서드를 사용합니다.
def __init__(self):
#메서드에 다른 인자들을 추가할 수 있습니다.
threading.Thread.__init__(self)
def run():
#코드내용
이제 준비는 다 끝났습니다. 쓰레드를 생성하고 시작하는 코드는 아래와 같습니다.
for i in range(50):
thread = DKThread()
thread.start()
이렇게 하면 50개의 스레드가 run() 메서드에 정의된 코드들을 각각 실행하게 됩니다. 간단하죠? :-) 하지만 문제가 하나 있습니다. 바로 각 스레딩에서의 자원 공유입니다. 스레드들은 병렬로 처리되기 때문에 하나의 자원을 공유할 때 동시성 문제가 발생할 수 있습니다(i.e. 하나의 스레드가 메서드 연산을 마치기 전에 다른 스레드가 해당 메서드를 호출하는 경우). 이런 것을 제어하기 위해 lock객체를 사용할 수 있습니다.
lock = threading.Lock()
lock에 대한 자세한 내용은 Python threading API에 잘 나와 있습니다. lock 에서 주로 쓰이는 메서드는 크게 두가지입니다.
lock.acquire(), lock.release()acquire()메서드는 다른 스레드가 acquire()이하의 코드들을 실행중이면 해당 메서드가 release()메서드를 만날때 까지 대기하도록 만들어 줍니다. 즉, 동시에 여러 스레드가 하나의 자원에 접근하지 못하도록 만들어 줍니다.
아! 그리고 스레드가 연산이 끝날때 까지 기다리는 join() 메서드를 호출해주는 것도 잊지 마세요~! join([timeout])메서드에는 timeout을 인자로 줘서 일정 시간이 지나서도 연산이 끝나지 않은 스레드를 강제로 종료할 수 있습니다.
30개의 스레드를 사용하여 0부터 999999까지 출력하는 예제
*. 쉬운 이해를 위해서 간단한 예를 들었습니다. 숫자를 출력하는 예제로는 단일 스레드와의 속도 차이를 거의 느끼지 못할 것입니다. 연산이 워낙 간단해서 거의 차이가 없습니다. 하지만 복잡한 연산을 개별 스레드에서 하고 연산 결과를 출력할 때만 lock을 걸어주면 단일 스레드와는 확연한 속도차이를 느끼실 수 있을것입니다. :-)
*참고링크 : 파이썬을 사용한 실전 스레드 프로그래밍 - 이 자료에서는 큐를 이용해서 자원을 공유하는데요. 몇몇 API가 2.5 버전 이상을 지원하더군요.. (현재 저는 2.4버전에서 작업중입니다. =ㅅ=a) 2.5버전 이상의 사용자들은 이 자료를 참고하시는게 좋으실듯 하네요 :-)
이올린에 북마크하기
이올린에 추천하기




댓글을 달아 주세요