파이썬 네트워크 프로그래밍 - 스레드

12117 단어
개인 독립 블로그: www.limiao.Tech Board
스레드 개념
루틴은 프로그램이 실행되는 과정에서 프로그램 코드를 실행하는 하나의 지점으로, 실행되는 프로그램마다 적어도 하나의 루틴이 있다
단일 스레드 실행
import time


def sing():
    for i in range(3):
        print("  ...%d" % i)
        time.sleep(1)

def dance():
    for i in range(3):
        print("  ...%d" % i)
        time.sleep(1)

if __name__ == '__main__':
    sing()
    dance()
    :

  ...0
  ...1
  ...2
  ...0
  ...1
  ...2

***Repl Closed***

다중 스레드 실행
다중 루틴의 실행은threading 모듈을 가져와야 합니다
매개변수 설명:
Thread([group[,target[,name[,args[,kwargs]]]]])
- group:    ,      None
- target:         
- args:              
- kwargs:             
- name:    ,      

멀티스레드 멀티태스킹
#      
import time, threading


def sing():
    #       
    print(threading.current_thread())
    for i in range(3):
        
        print("  ...%d" % i)
        time.sleep(1)

def dance():
    print(threading.current_thread())
    for i in range(3):

        print("  ...%d" % i)
        time.sleep(1)

if __name__ == '__main__':
    sing_thread = threading.Thread(target=sing)
    dance_thread = threading.Thread(target=dance)

    sing_thread.start()
    dance_thread.start()

  ...0

  ...0
  ...1
  ...1
  ...2
  ...2

***Repl Closed***

다중 루틴은 파라미터가 있는 작업을 실행합니다
import time, threading


def sing(num):
    for i in range(num):
        print("  ...%d" % i)
        time.sleep(1)


def dance(num):
    for i in range(num):
        print("  ...%d" % i)
        time.sleep(1)


if __name__ == '__main__':
    sing_thread = threading.Thread(target=sing, args=(3,))

    dance_thread = threading.Thread(target=dance, kwargs={"num": 3})

    sing_thread.start()
    dance_thread.start()
    :

  ...0
  ...0
  ...1
  ...1
  ...2
  ...2

***Repl Closed***

검색 스레드 목록 보기
import time, threading


def sing():
    for i in range(5):
        print("  ...%d" % i)
        time.sleep(1)


def dance():
    for i in range(5):
        print("  ...%d" % i)
        time.sleep(1)


if __name__ == '__main__':
    #              
    thread_list = threading.enumerate()
    print("111:", thread_list, len(thread_list))

    sing_thread = threading.Thread(target=sing)

    dance_thread = threading.Thread(target=dance)

    thread_list = threading.enumerate()
    print("222:", thread_list, len(thread_list))

    #     
    sing_thread.start()
    dance_thread.start()

    #        ,            
    thread_list = threading.enumerate()
    print("333:", thread_list, len(thread_list))
    :

111: [<_mainthread started="">] 1
222: [<_mainthread started="">] 1
  ...0
  ...0
333: [<_mainthread started="">, , ] 3
  ...1
  ...1
  ...2
  ...2
  ...3
  ...3
  ...4
  ...4

***Repl Closed***

주의
라인 사이의 집행은 무질서하다
import time, threading


def task():
    time.sleep(1)
    print("    :", threading.current_thread().name)

if __name__ == '__main__':

    for _ in range(5):
        sub_thread = threading.Thread(target=task)
        sub_thread.start()
    :

    : Thread-5
    : Thread-2
    : Thread-3
    : Thread-1
    : Thread-4

***Repl Closed***

메인 라인은 모든 하위 라인이 끝난 후에 끝납니다.
#                    
import time, threading


#                         
def show_info():
    for i in range(5):
        print("test:", i)
        time.sleep(1)


if __name__ == '__main__':
    sub_thread = threading.Thread(target=show_info)
    sub_thread.start()

    #      5 
    time.sleep(10)
    print("over")
    :

test: 0
test: 1
test: 2
test: 3
test: 4
over

***Repl Closed***

마스터 스레드 지키기
import time, threading


def show_info():
    for i in range(5):
        print("test:", i)
        time.sleep(1)

if __name__ == '__main__':
    #         ,                       
    sub_thread = threading.Thread(target=show_info, daemon=True)
    

    sub_thread.start()
    
    time.sleep(10)
    print("over")
    :

test: 0
test: 1
test: 2
test: 3
test: 4
over

***Repl Closed***

사용자 정의 스레드
import threading


#       
class MyThread(threading.Thread):
    #               
    def __init__(self, info1, info2):
        #          
        super().__init__()
        self.info1 = info1
        self.info2 = info2


    #             
    def test1(self):
        print(self.info1)


    def test2(self):
        print(self.info2)

    #   run        
    def run(self):
        self.test1()
        self.test2()


#        
my_thread = MyThread("  1", "  2")

#   
my_thread.start()
    :

  1
  2

***Repl Closed***


요약:
  • 사용자 정의 라인은 target을 지정할 수 없습니다. 사용자 정의 라인의 작업은 모두run 방법에서 실행되기 때문입니다
  • 시작 라인에서 start 방법을 통일적으로 호출하고run 방법을 직접 호출하지 마십시오. 이것은 하위 라인으로 작업을 수행하는 것이 아니기 때문입니다
  • 다중 스레드 공유 전역 변수
    import time, threading
    
    
    #       
    my_list = list()
    
    #       
    def write_data():
        for i in range(5):
            my_list.append(i)
            time.sleep(1)
        print("write_data:", my_list)
    
    #       
    def read_data():
        print("read_data:", my_list)
    
    if __name__ == '__main__':
        #          
        write_thread = threading.Thread(target=write_data)
        #          
        read_thread = threading.Thread(target=read_data)
    
        write_thread.start()
    
        #                         
        write_thread.join()
        print("      ...")
        read_thread.start()
    
        :
    
    write_data: [0, 1, 2, 3, 4]
          ...
    read_data: [0, 1, 2, 3, 4]
    
    ***Repl Closed***
    

    다중 스레드가 동시에 전역 변수를 조작하여 데이터에 오류가 발생할 수 있습니다
    import threading
    
    
    #       
    g_num = 0
    
    #           1
    def sum_num1():
        for i in range(1000000):
            global g_num
            g_num += 1
        print("sum1:", g_num)
    
    #           1
    def sum_num2():
        for i in range(1000000):
            global g_num
            g_num += 1
        print("sum2:", g_num)
    
    if __name__ == '__main__':
        #       
        first_thread = threading.Thread(target=sum_num1)
        second_thread = threading.Thread(target=sum_num2)
    
        first_thread.start()
        second_thread.start()   
    
        :
    
    sum1: 1491056
    sum2: 1528560
    
    ***Repl Closed***
    

    위의 실행 결과를 통해 알 수 있듯이 다중 루틴이 전역 변수 조작 데이터에 오류가 발생했다
    원인 분석: 두 라인firstthread 및 secondthread 전역 변수 gnum(기본값은 0)에서 1 연산을 진행하지만 다중 스레드가 동시에 작동하기 때문에 다음과 같은 상황이 발생할 수 있습니다.
    1.gnum=0시,firstthread 획득 gnum=0.이 때 시스템은firstthread를 "sleeping"상태로 조정하고secondthread가 "running"상태로 변환, t2도 g 획득num=0
    2. 그리고 secondthread에서 얻은 값을 1 더하기 및 gnum, gnum=1
    3. 그리고 시스템은 다시secondthread를 "sleeping"으로 조정하고firstthread에서 "running"으로 변경합니다.루틴 t1은 이전에 얻은 0 더하기 1을 g 에 부여한다num.
    4.이렇게 해서 퍼스트thread 및 secondthread 모두 gnum 더하기 1, 결과는 여전히 gnum=1.
    전역 변수 데이터 오류 해결 방법
    스레드 동기화: 같은 시간에 한 스레드만 전체 변수의 동기화를 조작할 수 있도록 보증한다. 바로 협동 보조로 예정된 선후 순서에 따라 운행한다.
    스레드 동기화 방법:
    1. 스레드 대기 (join)
    2. 상호 배척 자물쇠
    스레드 대기 실현 방법:
    import threading
    
    
    #       
    g_num = 0
    
    #           1
    def sum_num1():
        for i in range(1000000):
            global g_num
            g_num += 1
        print("sum1:", g_num)
    
    #           1
    def sum_num2():
        for i in range(1000000):
            global g_num
            g_num += 1
        print("sum2:", g_num)
    
    if __name__ == '__main__':
        #       
        first_thread = threading.Thread(target=sum_num1)
        second_thread = threading.Thread(target=sum_num2)
    
        first_thread.start()
        first_thread.join()
    
        second_thread.start()   
    
        :
    
    sum1: 1000000
    sum2: 2000000
    
    ***Repl Closed***
    

    결론: 여러 라인이 같은 전역 변수를 동시에 조작하면 자원 경쟁 데이터 오류가 발생할 수 있다
    스레드 동기화 방식은 자원 경쟁 데이터 오류 문제를 해결할 수 있지만, 이렇게 많은 임무가 단일 임무로 바뀌었다
    상호 배척 자물쇠
    공유 데이터에 잠금을 설정하여 같은 시간에 한 라인만 조작할 수 있도록 보증합니다
    자물쇠를 빼앗은 라인은 먼저 실행하고, 자물쇠를 빼앗지 않은 라인은 기다려야 하며, 자물쇠를 다 쓴 후에 풀어야 한다. 그리고 다른 기다리는 라인은 이 자물쇠를 뺏고, 어느 라인이 뺏고, 그 라인은 다시 실행해야 한다.
    구체적으로 어느 라인이 이 자물쇠를 뺏었는지 우리는 결정할 수 없다. CPU 스케줄링에 의해 결정된다
    스레드 동기화는 여러 스레드가 경쟁 자원에 안전하게 접근할 수 있도록 보장할 수 있다. 가장 간단한 동기화 메커니즘은 상호 배척 자물쇠를 도입하는 것이다.
    상호 배척 자물쇠가 자원에 도입된 상태: 잠금/비잠금
    어떤 라인이 공유 데이터를 변경하려고 할 때 먼저 그것을 잠그고 이때 자원의 상태는'잠금'이며 다른 라인은 변경할 수 없다.이 라인이 자원을 방출할 때까지 자원의 상태를 '비잠금' 으로 바꾸어야 다른 라인이 다시 자원을 잠글 수 있습니다.상호 배척 자물쇠는 매번 한 라인만 쓰기 작업을 진행하여 다중 라인 상황에서 데이터의 정확성을 확보한다.
    자물쇠를 만들려면 다음과 같이 하십시오.
    a = threading.Lock()
    잠기다
    a.acquire()
    풀어주다
    a.release()
    참고:
    1.만약 이 자물쇠가 잠기기 전에 잠기지 않았다면acquire는 막히지 않을 것이다
    2. 만약에 acquire를 호출하여 이 자물쇠에 잠기기 전에 다른 라인에 잠겼다면 이 자물쇠가 잠길 때까지 acquire는 막힐 것입니다.
    #        2             100     
    import threading
    
    
    #       
    g_num = 0
    
    #        
    lock = threading.Lock()
    
    #           1
    def sum_num1():
        #   
        lock.acquire()
        for i in range(1000000):
            global g_num
            g_num += 1
    
        print("sun1:", g_num)
    
        #    
        lock.release()
    
    #           1
    def sum_num2():
        #   
        lock.acquire()
        for i in range(1000000):
            global g_num
            g_num += 1
    
        print("sum2:", g_num)
    
        #    
        lock.release()
    
    if __name__ == '__main__':
        #     
        first_thread = threading.Thread(target=sum_num1)
        second_thread = threading.Thread(target=sum_num2)
    
        #     
        first_thread.start()
        second_thread.start()
    
        :
    
    sun1: 1000000
    sum2: 2000000
    
    ***Repl Closed***
    

    주의
    게다가 서로 밀어내는 자물쇠를 더하면 어느 라인이 이 자물쇠를 뺏었는지 우리는 결정할 수 없다. 어느 라인이 자물쇠를 뺏었는지 어느 라인이 먼저 집행하고 뺏지 않은 라인은 기다려야 한다.
    게다가 다중 임무가 순식간에 단일 임무로 바뀌면 성능이 떨어진다. 즉, 같은 시간에 한 라인만 실행할 수 있다는 것이다.
    상호 배척 자물쇠를 사용하는 목적
    여러 라인이 공유 데이터에 접근하여 자원 경쟁과 데이터 오류가 발생하지 않도록 보장할 수 있다
    잠금, 잠금 해제 과정
    하나의 라인에서 자물쇠를 호출하는 acquire () 방법으로 자물쇠를 얻었을 때, 자물쇠는'locked '상태로 들어갑니다.
    매번 하나의 라인만 자물쇠를 얻을 수 있습니다. 만약 다른 라인이 이 자물쇠를 얻으려고 시도한다면, 이 라인은 'Blocked' 상태로 바뀌어 '막힘' 이라고 합니다. 자물쇠가 있는 라인이 자물쇠를 호출하는release () 방법으로 자물쇠를 풀면 자물쇠는 'unlocked' 상태로 들어갑니다.
    스레드 스케줄러는 동기화 차단 상태의 스레드 중 하나를 선택하여 자물쇠를 가져오고 이 스레드를'running'상태로 진입시킵니다
    고정 자물쇠
    상대방이 자물쇠를 풀기를 기다리는 장면이 바로 자물쇠입니다.
    아래 표시에 따라 목록에서 값을 얻지만, 같은 시간에 한 라인만 값을 얻을 수 있음을 보증합니다
    #     :
    import time, threading
    
    #      
    lock = threading.Lock()
    
    def get_value(index):
        #   
        lock.acquire()
        print(threading.current_thread().name)
        my_list = [3, 6, 8, 1]
        #         
        if index >= len(my_list):
            print("    :", index)
            
            return
        value = my_list[index]
        print(value)
        time.sleep(1)
        #    
        lock.release()
    
    if __name__ == '__main__':
        #              
    
        for i in range(30):
            sub_thread = threading.Thread(target=get_value, args=(i,))
            sub_thread.start()
    

    잠금 해제:
    #     :
    import time, threading
    
    #      
    lock = threading.Lock()
    
    def get_value(index):
        #   
        lock.acquire()
        print(threading.current_thread().name)
        my_list = [3, 6, 8, 1]
        #         
        if index >= len(my_list):
            print("    :", index)
            lock.release()
            return
        value = my_list[index]
        print(value)
        time.sleep(1)
        #    
        lock.release()
    
    if __name__ == '__main__':
        #              
    
        for i in range(30):
            sub_thread = threading.Thread(target=get_value, args=(i,))
            sub_thread.start()
    

    소결: 서로 밀어내는 자물쇠를 사용할 때 자물쇠가 사라지는 문제에 주의해야 하며, 적당한 곳에서 자물쇠를 풀어주는 것에 주의해야 한다.
    사라진 자물쇠가 발생하면 응용의 정지 응답을 초래할 수 있습니다
    개인 독립 블로그: www.limiao.Tech Board

    좋은 웹페이지 즐겨찾기