Python/2.7 information

쓰레드,세마포어 기초

qkqhxla1 2014. 8. 12. 14:58

쓰레드 모듈. import threading으로 임포트합니다. import thread가 있긴하지만 

threading이 더 최신에 나온것이고 쓰기 쉽다고 어디 써있었는데 제가 둘다 써봤는데 threading모듈

내부에는 세마포어도 자동으로 있어서 더 쓰기가 쉬워요.

쓰레드 쓰기. 1. 쓰레드 인스턴스 생성해준다. 

ex) th = threading.Thread()같은 형식으로 만들어줍니다. th에는 만들어진 쓰레드 인스턴스가,

첫번째 인자는 쓰레드로 돌릴 함수 target=돌릴함수이름,

두번째 인자는 쓰레드로 돌릴 함수의 인자인데 여기서 인자로 튜플을 받습니다. args=() 이런형식

으로 받아요. 코드.

import urllib2,threading

def thread_even(i):
    print i

def thread_odd(i):
    print i

for i in range(0,10):
    if i%2==0:
        th = threading.Thread(target=thread_even,args=(i,))
        th.start()
    if i%2==1:
        th2 = threading.Thread(target=thread_odd,args=(i,))
        th2.start()

대충 해석이 되실겁니다. 0~10사이를 포문으로 돌리되 짝수면 thread_even이라는 쓰레드로 돌리고,

아니면 thread_odd라는 함수로 출력합니다. 여기서 인자를 줄 시에 한개면 반드시 끝에 ,를 붙여줘야

합니다. 이거 안 붙여주면 에러뜹니다. 이유를 몰라서 파이썬마을에 물어봤더니 이렇게해야

시퀀스의 하나인 튜플이 된다네요? 

*추가.

args=(i)의 경우 args=i와 같습니다. 하지만 args=(i,)의 경우 튜플 형태로 인자가 넘어갑니다. 그러므로 이렇게 해줘야 합니다. 예시로 2버전에서 print시 print 1과 print (1) 나 print((1))은 같은 1을 출력하지만, (1,)은 (1,)그대로를 출력합니다. 파이썬에서 튜플로 인식했다는 거죠. 튜플로 인식하게끔 하기 위해 이렇게 합니다.

그리고 만들어진 인스턴스.start()메소드로 쓰레드를 시작합니다. 이게 실질적으로 시작하는 

메소드에요. 쓰레드에서 따로 더 설명할건 없다고 봅니다. 첫번째 인자로 쓰레드로 돌릴 함수

두번째 인자로 넘길 인자를 튜플 형태로 한 후 start()메소드로 시작. 이게 기본입니다.

웹해킹시 쓰레드 모듈도 알아두는게 좋아요. 웹 레이스컨디션 문제도 있어서....

간단하게 세마포어에 관해서도 쓰겠습니다.

세마포어가 뭔지 모르시겠으면 이것저것 찾아보세요. 간단하게 설명하자면 두 쓰레드에서 한 변수에 

접근해서 값을 바꿀시 한 쓰레드가 값을 바꾸기도 전에 다른 쓰레드가 와서 값을 바꿔버리면 값이 

깨져버립니다. 그걸 방지하기 위해서 한 쓰레드만 값에 접근하도록 하는게 세마포어라고 아시면 

됩니다.

예제코드.

import threading

sum = 0

def thread_inc():
    global sum
    for i in range(0,50000):
        sum = sum + 1

def thread_des():
    global sum
    for i in range(0,50000):
        sum = sum - 1

thread_tuple=[]
for i in range(0,100):
    if i%2==0:
        th = threading.Thread(target=thread_inc,args=())
        thread_tuple.append(th)
        th.start()
    else:
        th2 = threading.Thread(target = thread_des, args=())
        thread_tuple.append(th2)
        th2.start()

for th in thread_tuple:
    th.join()
print 'sum =',sum

100번 반복하면서 짝수일때 sum의 값을 50000 증가, 홀수일때는 50000 감소시킵니다. 글로벌변수를

쓰기 때문에 두 쓰레드에서 한 값에 접근하는 예입니다. 생각대로라면 0이 나와야겠죠? 50000증가, 

50000감소를 똑같은 횟수대로 하니까요. 하지만 위에서 말했듯이 두 쓰레드가 한 값에 접근하면서

값이 깨져버려서 이상한 값이 출력됩니다.



매번 돌릴때마다 다르게 나올겁니다. 여기서 세마포어 만드는법과 간단한 설명을 하겠습니다.

세마포어 인스턴스는 이런식으로 만듭니다. sem = threading._Semaphore()

sem이라는 변수에 세마포어 인스턴스가 들어갑니다. 값을 들어갈때는 sem.acquire()로 들어가고,

나올때는 sem.release()로 나옵니다. 화장실에 들어갈때 잠그고 들어가서 끝날땐 열고 나오는거라

보시면 되요.

제대로 고쳐진 코드입니다.

import threading

sum = 0
sem = threading._Semaphore()

def thread_inc():
    global sum
    sem.acquire()
    for i in range(0,50000):
        sum = sum + 1
    sem.release()

def thread_des():
    global sum
    sem.acquire()
    for i in range(0,50000):
        sum = sum - 1
    sem.release()

thread_tuple=[]

for i in range(0,100):
    if i%2==0:
        th = threading.Thread(target=thread_inc,args=())
        thread_tuple.append(th)
        th.start()
    else:
        th2 = threading.Thread(target = thread_des, args=())
        thread_tuple.append(th2)
        th2.start()

for th in thread_tuple:
    th.join()

print 'sum =',sum



몇번을 돌려도 0이 제대로 나옵니다. 어디서 어떻게 잠궈야 하고 열어야 하느냐? 까지 설명하는건 

너무 벗어난 이야기기 때문에 더 알아보고 싶으시면 검색 해보세요.


'Python > 2.7 information' 카테고리의 다른 글

유효한 웹페이지 찾기?  (0) 2014.10.17
소켓과 urllib2 속도차이  (2) 2014.08.21
파이썬 소켓 기본.  (2) 2014.08.10
파이썬 리스트 관련  (0) 2014.08.10
md5, sha1, base64등의 인코딩,디코딩  (2) 2014.08.10