IT개발/Python(파이썬)2010. 8. 26. 16:30
프로세스(Process) vs 쓰레드(Thread)

동작하고 있는 프로세스라고 앞에서 설명하였다. 그렇다면, 쓰레드는 가벼운 프로세스라고 할 수 있다. 프로세스 안에서 실행되는 하나의 단위이다.
하나의 프로세스 안에는 여러개의 프로세스가 존재할 수 있다. 하나의 프로세스에 하나의 쓰레드만 있다면 싱글 쓰레드 프로세스(SingleThread Process), 여러개의 쓰레드가 돌고 있다면 멀디쓰레드 프로세스(MultiThread Process) 라고 한다.

특징
- 각각 독립적인 스택을 가지고 수행하나 코드나 자료는 공유한다.
- 쓰레드간의 자료 교환 및 공유는 프로세스 간의 자료 공유에 비해서 매우 쉽다.

쓰레드 사용하기 위해서는 저수준 모듈 thread, 고수준 모듈 threading을 지원한다. 이중에서 threading 모듈 사용을 권장한다.

■ thread 모듈

쓰레드 생성은 다음 메소드를 이용한다.
 start_new_thread(...)
        start_new_thread(function, args[, kwargs])

        Start a new thread and return its identifier.  The thread will call the
        function with positional arguments from the tuple args and keyword arguments
        taken from the optional dictionary kwargs.  The thread exits when the
        function returns; the return value is ignored.  The thread will also exit
        when the function raises an unhandled exception; a stack trace will be
        printed unless the exception is SystemExit.
- function : 실행될 함수명
- args : 함수의 인수
- kwargs : 키워드 인수[옵션]

예제1) 쓰레드 생성 : 5개의 쓰레드를 생성하고 각각이 문자열을 출력
import thread, time

# Thread 로 수행될 함수
# 이 함수에서 벗어나면 쓰레드로 종료된다.

def myThreadFunction(id):                                # 쓰레드 함수
        for i in range(4):
                print 'id %s -> count : %s' % (id , i)
                time.sleep(0.5)

for i in range(5):
        # 5개의 Thread 를 독립적으로 생성
        thread.start_new_thread(myThreadFunction, (i,))                      # 쓰레드를 생성되는 부분

time.sleep(3)
print 'Finish '
(실행 결과)
[citylock@nmsfile thread]$ ./genThread.py
id 0 -> count : 0
id 1 -> count : 0
id 2 -> count : 0
id 3 -> count : 0
id 4 -> count : 0
id 0 -> count : 1
id 1 -> count : 1
id 2 -> count : 1
id 3 -> count : 1
id 4 -> count : 1
id 0 -> count : 2
id 1 -> count : 2
id 2 -> count : 2
id 3 -> count : 2
id 4 -> count : 2
id 0 -> count : 3
id 1 -> count : 3
id 2 -> count : 3
id 3 -> count : 3
id 4 -> count : 3
Finish


2. 변수의 공유
- 쓰레드의 장점은 전역변수를 공유한다는 것이다.

예제2) 전역변수 g_count 를 모든 쓰레드가 값을 공유해서 사용하는 프로그램 작성
import thread, time

g_count = 0

def threadCount(id, count):
        global g_count             # g_count 를 전역 변수로 참조
        for i in range(count):
                print 'id %s ====> count : %s, g_count : %s' % (id, i, g_count)
                g_count = g_count + 1

for i in range(5):
        thread.start_new_thread(threadCount, (i,5))         # 쓰레드 생성

time.sleep(3)
print 'Final g_counter = ', g_count
print 'Finish Thread TEST Process'

(실행 결과)
[citylock@nmsfile thread]$ ./threadShareGlobal.py
id 0 ====> count : 0, g_count : 0
id 0 ====> count : 1, g_count : 1
id 0 ====> count : 2, g_count : 2
id 0 ====> count : 3, g_count : 3
id 0 ====> count : 4, g_count : 4
id 1 ====> count : 0, g_count : 5
id 1 ====> count : 1, g_count : 6
id 1 ====> count : 2, g_count : 7
id 1 ====> count : 3, g_count : 8
id 1 ====> count : 4, g_count : 9
id 2 ====> count : 0, g_count : 10
id 2 ====> count : 1, g_count : 11
id 2 ====> count : 2, g_count : 12
id 2 ====> count : 3, g_count : 13
id 2 ====> count : 4, g_count : 14
id 3 ====> count : 0, g_count : 15
id 3 ====> count : 1, g_count : 16
id 3 ====> count : 2, g_count : 17
id 3 ====> count : 3, g_count : 18
id 3 ====> count : 4, g_count : 19
id 4 ====> count : 0, g_count : 20
id 4 ====> count : 1, g_count : 21
id 4 ====> count : 2, g_count : 22
id 4 ====> count : 3, g_count : 23
id 4 ====> count : 4, g_count : 24
Final g_counter = 25
Finish Thread TEST Process
[citylock@nmsfile thread]$
- i 와 같은 지역변수는 쓰레드 간에 값을 공유하지 않는다.
- g_count 는 전역변수로 모든 쓰레드에서 참조해서 값을 사용할 수 있다.

위 예제에서는 g_count 값이 25로 제대로 된 값을 가졌다. 하지만 멀티쓰레드 연산의 경우 2개 이상의 쓰레드가 동시에 하나의 변수를 참조해서 쓰기작업을 하면 잘못된 결과를 가져올 수도 있다. (특히, 윈도우에서 심하다 ) 때문에 이를 해결하기 위해서는 변수를 LOCK 을 걸어서 사용하고 끝나면 다른 쓰레드에서 사용할 수 있도록 해제해 주어야 주는 작업을 할 수 있다.


■ 상호 배제 영역 (Critical Section)


앞에서 설명한 것처럼 하나의 쓰레드가 정보를 갱신하는 동안 다른 쓰레드가 그 변수를 접근하지 못하도록 해야 한다.
변수를 락, 해제 관련된 메소스
help> thread
     |  acquire() -- lock the lock, possibly blocking until it can be obtained
     |  release() -- unlock of the lock
     |  locked() -- test whether the lock is currently locked
     |
     |  A lock is not owned by the thread that locked it; another thread may
     |  unlock it.  A thread attempting to lock a lock that it has already locked
     |  will block until another thread unlocks it.  Deadlocks may ensue.

global 변수를 락하는 방법
lock.acquire()                  #  락을 얻고 들어간다. 다른 쓰레드가 사용하고 있으면 자동으로 대기한다.
g_count = g_count + 1       #  상호배제 영역 (Critical Section)
lock.release()                  #  락을 해제한다. 다른 쓰레드가 사용가능 하도록 허락한다.


■ 쓰레드 종료 판단 하기

쓰레드 종료 관련해서 표준 라이브러리가 지원되지 않기 때문에 공유변수를 이용해서 처리한다.

예제) 쓰레드 종료 판단하기
# threadFinishOne.py  : 쓰레드 종료 판단하기

import thread, time

NoOfThread = 5
threadExit = [0] * NoOfThread

def counter(id, count):
        for i in range(count):
                print 'id %s --> count : %s' % (id, i)
        threadExit[id] = 1

for i in range(NoOfThread):
        thread.start_new_thread(counter, (i,5))

while 0 in threadExit:
        time.sleep(0.1)

print 'Finish', threadExit

예제2) 개별 쓰레드의 존재보다는 남아 있는 쓰레드 수에만 관심이 있다면 아래 코드를 사용
import thread, time

NoOfThread = 5
threadLeft = NoOfThread      # 남아있는 쓰레드 수
lock = thread.allocate_lock()

# 쓰레드 종료시 호출되는 뒤처리 함수
# 남아있는 쓰레드수 1 감소
def threadexit(id):
        global threadLeft
        print 'thread %d is quitting' % id
        lock.acquire()           #  작업 권한 요청
        threadLeft -= 1
        lock.release()           # 락 해지.

def counter(id, count):
        for i in range(count):
                print 'id %s --> count : %s' % (id, i)
        threadexit(id)

for i in range(NoOfThread):
        thread.start_new_thread(counter, (i,5))

while threadexit:
        time.sleep(0.1)

print 'Finish'

(실행결과)
[citylock@nmsfile thread]$ ./threadFinishTwo.py
id 0 --> count : 0
id 0 --> count : 1
id 0 --> count : 2
id 0 --> count : 3
id 0 --> count : 4
thread 0 is quitting
id 1 --> count : 0
id 1 --> count : 1
id 1 --> count : 2
id 1 --> count : 3
id 1 --> count : 4
thread 1 is quitting
id 2 --> count : 0
id 2 --> count : 1
id 2 --> count : 2
id 2 --> count : 3
id 2 --> count : 4
thread 2 is quitting
id 3 --> count : 0
id 3 --> count : 1
id 3 --> count : 2
id 3 --> count : 3
id 3 --> count : 4
thread 3 is quitting
id 4 --> count : 0
id 4 --> count : 1
id 4 --> count : 2
id 4 --> count : 3
id 4 --> count : 4
thread 4 is quitting









Posted by 시티락