제15장, Python 다중 스레드 동기화 자물쇠, 고정 자물쇠와 귀속 자물쇠

6241 단어
카탈로그
  • 제15장, Python 다중 스레드 동기화 자물쇠, 고정 자물쇠와 귀속 자물쇠
  • 1. 인용자:
  • 2.동기식 잠금
  • 3.고정 자물쇠
  • 인자:
  • 4.재귀적 잠금 RLock
  • 원리:
  • 긴 말 말고 코드 넣기
  • 요약:
  • 5. 대총결산

  • 제15장, Python 다중 스레드 동기화 자물쇠, 고정 자물쇠와 귀속 자물쇠


    1. 도입부:

    1. 
    t1 = threading.Thread(target=say,args=('tony',))
    2. 
    t1.start()
     join 

    이상은python 다중 루틴의 기본 사용입니다
    설명: 앞에서 말한 두 가지 기능은 서로 독립적이고 서로 간섭하지 않으며 같은 향유하는 자원이나 데이터를 사용하지 않는다. 만약에 우리가 여러 라인에서 같은 데이터를 사용하려면 자원의 사용과 잠금 문제가 존재한다. 어떤 언어에서든 이것은 피할 수 없는 것이다.그러면 이어서 동기화 자물쇠, 고정 자물쇠와 귀속 자물쇠의 사용에 대해 이야기합시다.

    2. 동기식 잠금


    자물쇠는 일반적으로 공유 자원에 대한 동기화 접근을 실현하는 데 사용된다.모든 공유 자원에 Lock 대상을 만듭니다. 이 자원에 접근할 때 acquire 방법을 사용해서 자물쇠 대상을 가져옵니다. (다른 라인이 이 자물쇠를 얻었으면 현재 라인이 풀리기를 기다려야 합니다.) 자원 접근이 끝난 후에release 방법을 사용해서 자물쇠를 풀십시오.
    동기식 잠금이 적용되는 예는 다음과 같습니다.
    import threading
    import time
    
    num = 100
    
    def fun_sub():
        global num
        # num -= 1
        num2 = num
        time.sleep(0.001)
        num = num2-1
    
    if __name__ == '__main__':
        print('  at %s' % time.ctime())
    
        thread_list = []
        for thread in range(100):
            t = threading.Thread(target=fun_sub)
            t.start()
            thread_list.append(t)
    
        for t in thread_list:
            t.join()
        print('num is %d' % num)
        print('  at %s' % time.ctime())
    -----------------------------------------------------
      at Sun Apr 28 09:56:45 2019
    num is 91
      at Sun Apr 28 09:56:45 2019

    이러한 예는 다음과 같다. 100개의 라인을 만들고 모든 라인은 공공자원num변수에서 감1작업을 수행한다. 정상적인 상황에서 코드가 실행될 때까지 기다렸다가num변수를 출력하면 0을 얻을 수 있다. 왜냐하면 100개의 라인이 모두 감1작업을 한 번 실행했기 때문이다.
    이런 문제는 0이 아니라 91이라는 걸 발견했다는 거예요.
    코드 아이디어를 정리해 보겠습니다.
    1. GIL 때문에 하나의 라인(라인 1 가정)만num이라는 자원을 얻은 다음에 변수를num2,sleep 0.001초에 부여한다. 이때num=1002.첫 번째 스레드sleep 0.001초 동안 이 스레드는 yield 조작을 할 것이다. 바로 cpu를 다른 스레드로 전환하여 실행하는 것이다. (스레드 2가 GIL을 얻고 cpu 사용권을 얻는다고 가정하면) 스레드 2도 스레드 1과 마찬가지로num을 받고num2에 할당된 값을num2로 되돌려준다. 그리고sleep, 이때num은 사실은=100이다.3. 라인 2 sleep 때 또 yield 조작을 해야 한다. 라인 3이num을 받았다고 가정하고 위의 조작을 실행하면 사실num은 1004.뒤에 cpu가 다시 라인 1, 라인 2, 라인 3으로 전환될 때 그들은 1 감소 작업을 실행한 후에 사실 기다린num은 모두 99이지 순서가 줄어드는 것이 아니다.5. 기타 남은 라인 조작은 위와 같다
    해결 방안:python의 동기화 자물쇠를 빌려야 한다. 즉, 같은 시간에 하나의 라인만 놓아서num변수를 조작할 수 있다. 1을 줄인 후에 뒤의 라인은num변수를 조작한다.다음에 우리가 어떻게 실현하는지 보자.
    import threading
    import time
    
    num = 100
    
    def fun_sub():
        global num
        lock.acquire()
        print('---- ----')
        print(' :',t.name)
        num2 = num
        time.sleep(0.001)
        num = num2-1
        lock.release()
        print('---- ----')
    
    if __name__ == '__main__':
        print('  at %s' % time.ctime())
    
        lock = threading.Lock() # 
    
        thread_list = []
        for thread in range(100):
            t = threading.Thread(target=fun_sub)
            t.start()
            thread_list.append(t)
    
        for t in thread_list:
            t.join()
        print('num is %d' % num)
        print('  at %s' % time.ctime())
     ------------------------------------------------
     .......
    ---- ----
     : Thread-98
    ---- ----
    ---- ----
     : Thread-100
    ---- ----
    num is 0
      at Sun Apr 28 12:08:27 2019

    사고방식: 위에서 우리가 중간에 있는 1 코드 블록을 줄이고 동기화 자물쇠를 추가하는 것을 보면 우리가 원하는 결과를 얻을 수 있다. 이것이 바로 동기화 자물쇠의 역할이다. 한 번에 하나의 라인만 조작하여 자원을 공유하는 것이다.

    3. 고정 자물쇠


    도입부:


    사라진 자물쇠라는 개념은 여러 곳에 존재하는데, 데이터에 비교해 보면, 사라진 자물쇠가 어떻게 생겼는지 대충 소개한다.
    #  1 ( 2) ( 1),
    #  2 ( 1) ( 2)
    #  , .
    #  

    발생 원인:python에서 온라인 스레드에서 여러 개의 자원을 공유할 때 두 스레드가 각각 일부 자원을 차지하고 상대방의 자원을 동시에 기다리면 자물쇠가 사라진다. 시스템이 이 부분의 자원을 모두 사용하고 있다고 판단하기 때문에 이 두 스레드는 외부의 힘이 없는 상황에서 계속 기다릴 것이다.
    from threading import Thread,Lock
    mutex1 = Lock()
    mutex2 = Lock()
    import time
    class MyThreada(Thread):
        def run(self):
            self.task1()
            self.task2()
        def task1(self):
            mutex1.acquire()
            print(f'{self.name}    1 ')
            mutex2.acquire()
            print(f'{self.name}    2 ')
            mutex2.release()
            print(f'{self.name}    2 ')
            mutex1.release()
            print(f'{self.name}    1 ')
    
        def task2(self):
            mutex2.acquire()
            print(f'{self.name}    2 ')
            time.sleep(1)
            mutex1.acquire()
            print(f'{self.name}    1 ')
            mutex1.release()
            print(f'{self.name}    1 ')
            mutex2.release()
            print(f'{self.name}    2 ')
    
    for i in range(3):
        t = MyThreada()
        t.start()

    그러면 이 잠금 문제를 해결하기 위해 귀속 잠금 방안을 도입하였다

    4. 재귀적 잠금 RLock


    원리:


    같은 라인에서 같은 자원을 여러 번 요청하는 것을 지원하기 위해python은 '귀속 잠금':threading을 제공합니다.RLock.RLock 내부에는 Lock과 counter 변수가 유지되고 있으며,counter는 acquire의 횟수를 기록하여 자원을 여러 번 acquire에 저장할 수 있습니다.한 라인의 모든 acquire가release에 의해 다른 라인이 자원을 얻을 때까지

    긴말하지 말고 코드를 놓아라

    from threading import Thread,Lock,RLock
    # mutex1 = Lock()
    # mutex2 = Lock()
    mutex1 = RLock()
    mutex2 = mutex1
    
    import time
    class MyThreada(Thread):
        def run(self):
            self.task1()
            self.task2()
        def task1(self):
            mutex1.acquire()
            print(f'{self.name}    1 ')
            mutex2.acquire()
            print(f'{self.name}    2 ')
            mutex2.release()
            print(f'{self.name}    2 ')
            mutex1.release()
            print(f'{self.name}    1 ')
    
        def task2(self):
            mutex2.acquire()
            print(f'{self.name}    2 ')
            time.sleep(1)
            mutex1.acquire()
            print(f'{self.name}    1 ')
            mutex1.release()
            print(f'{self.name}    1 ')
            mutex2.release()
            print(f'{self.name}    2 ')
    
    
    for i in range(3):
        t = MyThreada()
        t.start()

    요약:


    위에서 우리는 하나의 귀속 자물쇠를 사용하여 여러 개의 동기 자물쇠로 인한 고정 자물쇠 문제를 해결하였다.RLock은 큰 자물쇠에 작은 자물쇠가 있다는 것을 이해할 수 있다. 내부의 모든 작은 자물쇠가 없어질 때까지 기다려야 다른 라인이 이 공공 자원에 들어갈 수 있다.

    5. 총결산


    그리고 모든 다중 노드에 데이터가 동기화되지 않고 잠겨 있지 않은 문제가 존재하는 것은 아니지만 공유 자원을 방문할 때 잠금이 반드시 존재해야 한다. 그래서 우리가 코드에 잠금을 넣을 때 어디에 잠그는지 주의해야 한다. 성능에 대한 영향이 가장 적다. 이것은 논리에 대한 이해에 의존한다.

    좋은 웹페이지 즐겨찾기