ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Python 문법 기초 34 - thread(자원공유, 활성화/비활성화)
    Python 2022. 10. 23. 13:52
    # process : 실행 중인 프로그램을 의미함. 자신만의 메모리를 확보하고 공유하지 않음.
    # thread : light weight process라고도 함. 하나의 process 내에는 한 개의 thread가 존재함
    # process 내에 여러 개의 thread를 운영하여 여러개의 작업을 동시에 하는 것처럼 느끼게 할 수 있다.
    # multi thread로 multi tasking이 가능
    # 데이터를 원할하게 송수신 하기 위해서는 thread를 사용하는 것이 좋다.
    
    import threading, time
    
    def run(id):
        for i in range(1,51):
            print('id:{} --> {}'.format(id, i))
            time.sleep(0.2)
            
    # thread를 사용하지 않은 경우
    # run(1)
    # run(2)
    
    # thread를 사용한 경우
    # threading.Thread(target=수행함수명)
    th1 = threading.Thread(target=run, args=('일',)) # args는 꼭 tuple 타입이어야만 된다.
    th2 = threading.Thread(target=run, args=('둘',))
    th1.start()
    th2.start()
    
    print('프로그램 종료')

    thread를 사용하지 않은 경우에는 실행 순서대로 차례대로 반복문이 돈다.

    thread를 사용한 경우에는 th1과 th2가 같이 랜덤으로 돈다. 프로그램이 종료가 되도 thread가 계속된다. 그러므로 th2가 먼저 실행되도 랜덤으로 돌기때문에 상관없다.

    thread의 종료 함수는 없으므로 thread가 사용 중인 함수가 종료되면 종료된다.

     

    th1.join()
    th2.join()

    start() 함수 밑에 위의 join() 함수를 넣으면 thread가 끝날 때까지 main 프로그램이 종료되지 않는다.

     

     

    예제) thread를 사용하여 디지털 시계 만들기

    # thread를 사용한 디지털 시간 출력
    import time
    
    now = time.localtime()
    print(now)
    print('{}년 {}월 {}일 {}시 {}분 {}초'.format(
        now.tm_year, now.tm_mon, now.tm_mday, now.tm_hour, now.tm_min, now.tm_sec))
        
    print('---------')
    import threading
    
    def cal_show():
        now = time.localtime() # /란 다음 줄까지 이어진다는 의미
        print('{}년 {}월 {}일 {}시 {}분 {}초'.format( \
            now.tm_year, now.tm_mon, now.tm_mday, now.tm_hour, now.tm_min, now.tm_sec))
    
    def my_run():
        while True:
            now2 = time.localtime()
            if now2.tm_min == 4: break
            cal_show()
            time.sleep(1)
            
    th = threading.Thread(target=my_run)
    th.start()
    
    th.join()
    print('프로그램 종료')

     

    time.sleep과 반복문을 주어 1초마다 갱신되도록 설정하였다.

     

    # 여러 thread 간 공유자원 충돌 방지
    # 동기화(줄서기) - 하나의 thread가 자원을 사용하는 동안 다른 thread는 공유자원 사용을 대기
    
    import threading, time
    
    g_count = 0 # 전역변수는 자동으로 thread의 공유자원이 된다.
    lock = threading.Lock() # 동기화(줄서기) 기능 함수
    
    def threadCount(id, count):
        global g_count
        for i in range(count):
            lock.acquire() # thread 간 충돌 방지용. 현재 thread가 공유자원을 점유하고 있는 동안 다른 thread는 대기 상태
            print('id:%s ===> count:%s, g_count:%s'%(id, i, g_count))
            time.sleep(0.1)
            g_count += 1
            lock.release() # 공유자원을 점유 해제
            
    for i in range(1, 6):
        threading.Thread(target=threadCount, args=(i, 5)).start()
        
    time.sleep(5)
    print('처리 후 최종 g_count : ', g_count)
    print('프로그램 종료')
    
    <console>
    id:1 ===> count:0, g_count:0
    id:1 ===> count:1, g_count:1
    id:1 ===> count:2, g_count:2
    id:1 ===> count:3, g_count:3
    id:1 ===> count:4, g_count:4
    id:2 ===> count:0, g_count:5
    id:3 ===> count:0, g_count:6
    id:3 ===> count:1, g_count:7
    id:5 ===> count:0, g_count:8
    id:5 ===> count:1, g_count:9
    id:5 ===> count:2, g_count:10
    id:5 ===> count:3, g_count:11
    id:5 ===> count:4, g_count:12
    id:4 ===> count:0, g_count:13
    id:4 ===> count:1, g_count:14
    id:4 ===> count:2, g_count:15
    id:4 ===> count:3, g_count:16
    id:4 ===> count:4, g_count:17
    id:3 ===> count:2, g_count:18
    id:3 ===> count:3, g_count:19
    id:3 ===> count:4, g_count:20
    id:2 ===> count:1, g_count:21
    id:2 ===> count:2, g_count:22
    id:2 ===> count:3, g_count:23
    id:2 ===> count:4, g_count:24
    처리 후 최종 g_count :  25
    프로그램 종료

    lock 함수를 사용하여 g_count가 충돌되지 않도록 설정하였다.

    전역변수는 공유자원이다. 충돌이 일어날 수 있으므로 그것을 방지하기 위해서 lock을 쓰는 것이다.

    lock 함수를 사용하면 나중에 풀어주어야 된다. lock.release 를 사용하여 풀 수 있다.

    args 에서 숫자를 부여한 만큼 thread가 사용된다. 반복문을 돌면 그 만큼 적용이된다.

     

    # thread 간 자원공유 + thread 활성화/비활성화
    
    import threading, time
    
    bread_plate = 0 # 빵 접시
    lock = threading.Condition()
    
    class Maker(threading.Thread): # 생산자
        def run(self):
            global bread_plate
            for i in range(30):
                lock.acquire() # 공유자원 점유
                while bread_plate >= 10:
                    print('빵 생산 초과로 대기')
                    lock.wait()  # thread의 비활성화
                bread_plate += 1
                print('빵생산 : ', bread_plate)
                lock.notify()  # thread의 활성화
                lock.release()  # 공유자원 점유 해제
                time.sleep(0.05)
                
    class Consumer(threading.Thread): # 소비자
        def run(self):
            global bread_plate
            for i in range(30):
                lock.acquire() # 공유자원 점유
                while bread_plate < 1:
                    print('빵 소비 초과로 대기')
                    lock.wait()  # thread의 비활성화
                bread_plate -= 1
                print('빵소비 : ', bread_plate)
                lock.notify()  # thread의 활성화
                lock.release()  # 공유자원 점유 해제
                time.sleep(0.05)
    
    mak = []; con = []
    for i in range(5): # 생산자 수
        mak.append(Maker())
        
    for i in range(5): # 소비자 수
        con.append(Consumer())
        
    for th1 in mak:
        th1.start()
        
    for th2 in con:
        th2.start()
        
    for th1 in mak:
        th1.join()
        
    for th2 in con:
        th2.join()
        
    print('오늘 영업 끝~~~')
    
    <console>
    빵소비 :  4
    빵소비 :  3
    빵소비 :  2
    빵소비 :  1
    빵소비 :  0
    빵 소비 초과로 대기
    빵 소비 초과로 대기
    빵 소비 초과로 대기
    빵 소비 초과로 대기
    빵생산 :  1
    빵생산 :  2
    빵소비 :  1
    빵생산 :  2
    빵소비 :  1
    빵생산 :  2
    빵소비 :  1
    빵소비 :  0
    빵생산 :  1
    빵소비 :  0
    빵 소비 초과로 대기
    빵생산 :  1
    빵소비 :  0
    빵 소비 초과로 대기
    빵생산 :  1
    빵소비 :  0
    빵 소비 초과로 대기
    빵생산 :  1
    빵소비 :  0
    빵 소비 초과로 대기
    빵 소비 초과로 대기
    빵생산 :  1
    빵생산 :  2
    빵소비 :  1
    빵소비 :  0
    빵 소비 초과로 대기
    빵생산 :  1
    빵소비 :  0
    빵 소비 초과로 대기
    빵생산 :  1
    빵소비 :  0
    빵생산 :  1
    빵생산 :  2
    빵생산 :  3
    빵소비 :  2
    빵소비 :  1
    빵소비 :  0
    빵 소비 초과로 대기
    빵생산 :  1
    빵소비 :  0
    빵 소비 초과로 대기
    빵생산 :  1
    빵생산 :  2
    빵생산 :  3
    빵생산 :  4
    빵소비 :  3
    빵소비 :  2
    빵소비 :  1
    빵소비 :  0
    오늘 영업 끝~~~

    lock.acquire() , lock.release() 가 한 쌍으로 쓰인다.

    lock.wait(), lock.notify() 가 한 쌍으로 쓰인다.

    전역변수인 bread_plate를 공유하여 사용하였다.

     

     

    댓글

Designed by Tistory.