파이썬 뮤텍스 잠금 (잠금) : 멀티 스레드 안전 문제를 해결하기 위해

멀티 스레딩의 장점은 동시에 여러 작업을 실행할 수 동시성이다. 스레드가 공유 데이터를 필요로 할 때, 그것은 인해 발생하는 쓰레드 스케줄링 시스템에 의해 특정 임의성있는 데이터에 생산 "거짓 사건을"동기화되지 않을 수 있습니다.

뮤텍스의 역할은 데이터 동기화 문제를 해결하는 것입니다. 뮤텍스에 대해서, 고전 "은행 돈"문제가있는 것입니다. 은행 돈 기본 프로세스는 다음 단계로 나눌 수 있습니다 :

  1. 사용자 계정 암호를 입력, 시스템은 사용자의 계정, 암호 일치를 결정합니다.
  2. 사용자 입력 인출 금액.
  3. 시스템은 계정 잔액이 인출 량을 초과 하는지를 판정한다.
  4. 균형 철수, 성공적인 철수의 양보다 크다면, 나머지는 철수의 양보다 작은 경우, 철수는 실패합니다.


언뜻보기에,이 일상 생활에서 철수 과정, 아무 문제없이이 과정은 참입니다. 이 과정은 멀티 스레드 시나리오 아래에 배치됩니다하지만, 일단 발생할 수있는 문제점이있다. 여기에 말을하는 것이 가능하다는 것을 참고, 확실하지 않다. 아마도 당신은 문제없이 백만 번을 실행하고 있지만 문제가 의미하는 것은 아니다 발생하지 않았 아무 문제가 없다!

위의 절차 철수 프로그램에 따라 준비하고 사용하는 두 개의 스레드가 동일한 계정의 동시 작업을 할 돈을 두 사람을 사용하여 시뮬레이션 하였다. 확인 계정과 암호, 시뮬레이션 뒤에 세 개의 단계에서 현재 운영. 다음 계정이 클래스를 정의하는 클래스 캡슐화는 계좌 번호 및 잔액 두 멤버 변수를 차지한다.

클래스 계정 :
     #이 생성자를 정의 
    DEF  __init__ (자체, account_no, 밸런스) :
         #의 패키지 계좌 번호, 계좌 잔고를 두 멤버 변수 
        self.account_no = account_no 
        self.balance = 균형

다음으로, 시뮬레이션 프로그램은 기능의 동작 개수에 따라, 함수 인출 정의 로직 잔액이 불충분 현금 계정이 아닐 경우 지폐 토출 시스템 밸런스가 충분히 균형을 줄일 경우, 출금, 돈 돈 계정을 인출 수행 .

주요 프로그램은 매우 간단합니다, 그냥 계정을 생성하고 계정에서 돈을 인출하는 두 개의 스레드를 시작합니다. 다음 절차는 다음과 같습니다

가져 오기 스레딩
 가져 오기 시간
 가져 오기 계정
 #은 돈을 인출하는 작업을 시뮬레이션하는 함수를 정의 
DEF : (계정, draw_amount) 그리기
     #의 계정 잔액이 인출의 수보다 큰 
    IF account.balance> = : draw_amount
         #의 배출 지폐 
        인쇄 (threading.current_thread () 이름입니다. \
             + " 인출 성공 침 법안 :! " + STR (draw_amount))
 #         time.sleep (0.001) 
        # 밸런스 수정 
        account.balance를 - = draw_amount의
         인쇄 ( " \ t 균형은 : " + STR (account.balance))
    다른 :
         인쇄 (. threading.current_thread () 이름 \
             + " 돈을 수집하는 방법이 부족 자금을 실패! " )
 #이 계정 만들기 
(= Account.Account ACCT를 " 1234567 " , 1000 )
 #이 돈을 인출 할 수있는 동일한 계정에 두 개의 스레드를 시뮬레이션 
threading.Thread의 (NAME = " ' = 대상 인수 = (ACCT 800 그린다 . (시작))) 
threading.Thread의 (이름 = ' B ' = 대상 인수 = (ACCT 그리기, 800)). 시작 ()

주석 프로그램 코드의 첫 번째 파이프 라인 (12)를 수행, 위의 절차는 아주 간단한 논리 인출, 논리는 실제 작동도 매우 유사하다 돈을 인출를 철회.

반복 당신은 가능성이 그림 1의 오류 결과를 볼 것이다, 위의 프로그램을 실행합니다.

성공적인 걸릴 돈! 법안을 뱉어 : 800
B 인출 성공! 법안을 뱉어 : 800
균형은 다음과 같습니다 (200)
잔액은 다음과 같습니다 -600을

멀티 스레드 때문에 불확실성 스레드 스케줄링 "실수"에러의 급격한 출현 프로그래밍되는, (올바른 동작 결과를 참조 할 수있다하더라도) 뱅크에 나타낸 결과는 원하는 결과를 실행하지 않는다.

가정이 시스템 스레드 일시 정지에 코드의 주석에서 스케줄러, 그래서 (강제 일시 중지하려면만큼 코멘트에 프로그램 코드 주석의 취소) 다른 스레드 실행. 주석 후 다시 프로그램을 실행, 결과는 항상 그림 1의 오류를 볼 수 있습니다.

문제는, 1천6백위안 만 1천위안에서 균형 계정 발생하지만, 부정적인 계정 잔액이 있었다, 은행은 지금까지 예상 된 결과에서입니다. 상기 과정을 인위적 사용하는 동안  time.sleep(0.001) 스레드 스케줄링 스위치를 강제로하지만, 스위치 완전히 가능성이 발생하는 것이다 (100,000 동작 긴 에러가 발생되면만큼, 그는 프로그램 오류로 인해).

파이썬 뮤텍스 동기화 스레드

메소드 본문의 run () 메소드가 보안 실이 없기 때문에 그림 1 오류에 표시된 결과가 생겨났다, 프로그램은, 두 개의 스레드가 동시에 계정 개체를 수정할 수 있으며,이 시스템은 단지 코드 실행 스레드 개의 댓글 전환 계정 개체를 수정하는 다른 스레드로 전환, 그래서 문제가 있습니다.

이 문제를 해결하기 위해, 파이썬의 스레딩 모듈은 뮤텍스 락 (잠금)을 소개합니다. 잠금 나사와 모듈은 두 종류 첨가 뮤텍스 뮤텍스를 해제하기위한 다음의 두 가지 방법을 제공 RLock을 제공한다 :

  1. 또는 잠금 시간 매개 변수는 초 단위로 지정 RLock 상기 잠금 고정하기위한 요구 : 획득은 (= TRUE, 초과 = -1 차단).
  2. 릴리스 () : 잠금을 해제.


다음과 같이 잠금 및 RLock 차이는 :

  • threading.Lock과 : 그것은 잠금 요청의 나머지는 해제 잠금을 얻기 위해 대기하는 기본적인 락 객체는 각 시간을 고정 할 수 있습니다.
  • threading.RLock : 그것은 재진입 잠금 (재 호출 잠금)을 나타냅니다. 재진입 자물쇠를 들어, 쓰레드는 잠금 같은 여러가 여러 번 해제 될 수있을 수 있습니다. 당신이 RLock를 사용하는 경우, 취득 ()과 해제 () 메소드는 쌍을 이루어야합니다. 당신이 잠금 전화를 n 번 획득을 ()하는 경우에는 잠금을 해제 할 수있는 n 번째 릴리스 ()를 호출해야합니다.


따라서 RLock 잠금 재진입을 갖는. 즉, 동일한 스레드를 다시 잠겨 RLock 잠금 고정 될 수 RLock 객체 ()에있어서, 취득하는 각각의 호출 후에 스레드 () 잠금을 모두 중첩 호출 획득하고 추적하는 카운터를 유지 명시 적으로 잠금을 해제 할 수있는 자료 () 메서드를 호출해야합니다. 따라서, 동일한 방법 로크의 로크 보호 부 추가 메소드 호출을 보호 할 수있다.

잠금 액세스 공유 리소스에 다중 스레드를 제어하기위한 도구입니다. 일반적으로 잠금이 공유 자원에 대한 독점적 인 액세스를 제공 잠금 객체에, 당신은 하나의 스레드 잠금을 가질 수 요청은 잠금 개체의 공유 리소스에 대한 액세스 권한을 얻기 위해 시작 부분에 스레드가 선행되어야한다. 공유 자원에 대한 완전한 액세스는, 프로그램은 잠금 개체의 잠금을 해제합니다.

스레드 안전 제어가 실현에서 더 일반적으로 사용되는 RLock입니다. 다음과 같이 RLock 일반적으로 사용되는 코드의 형식은 다음과 같습니다

클래스 X-는 :
     #의 정의는 스레드 안전 방식이 필요 
    DEF의 : m ()
         #의 잠금 
        self.lock.acquire ()
         은 try를 :
             #이 스레드 안전 코드가 필요 
            # ... 방법 바디
         #을 분리 잠금을 보장하기 위해 마지막으로 차단하려면 사용 
        마지막으로 :
             # 수정 완료, 잠금 해제 
            ) (self.lock.release를

사용 RLock 객체는 보안 스레드를 제어하고, 잠금은 일반적으로 결국 필요한 경우 잠금이 해제되도록 차단 사용하는 것이 좋습니다 다른 범위의 역할에 나타날 때 잠금이 해제됩니다.

다음과 같은 특성을 갖는 스레드 안전 형, 스레드 안전 유형을 달성하기 아주 쉬운을 사용하여 객체를 잠금 :

  • 그 클래스의 객체가 안전하게 여러 스레드에 액세스 할 수 있습니다.
  • 객체의 메소드를 호출 한 후 각 스레드, 당신은 올바른 결과를 얻을 것이다.
  • 객체의 메소드를 호출 한 후 각 스레드는, 객체는 적절한 상태로 남아있다.


일반적으로, 변경할 수 없습니다 객체의 상태이기 때문에 불변 클래스는 항상 안전 스레드 있지만 변경 가능한 객체는 스레드 안전을 보장하기 위해 추가적인 방법을 필요로한다. 두 돈 동시에 변경 될 때, 예를 들어 상기 계좌는 가변 클래스이며, 두 멤버 변수가 변경 될 수있다 (더 포장 밸런스 변경 _balance 위해) 그 self.account_no의 self._balance 경우 멤버 변수 self._balance 계정 개체의 값은, 프로그램이 비정상적으로 나타났다. self.balance에 계정 수준의 액세스가 스레드 안전을 위해 설정 아래, 단순히 self.balance 스레드 안전 수정할 수있는 제어 방법을 증가시킵니다.

양식을 다음과 같이 계정 클래스는 읽기, 그것은 스레드 안전합니다 :

가져 오기 스레딩
 가져 오기 시간
 클래스 계정 :
     #이 생성자를 정의 
    DEF  __init__ (자체, account_no, 밸런스) :
         #의 패키지 계좌 번호를 균형 계정이 개 멤버 변수 
        self.account_no = account_no 
        self._balance = 밸런스 
        self.lock = threading.RLock ()
     # 계정 잔액이 쉽게 수정을 허용하지 않기 때문에, self._balance 너무에만 게터 방법 
    DEF getBalance (자동) :
         리턴 self._balance의
     #은 돈 철회 할 수있는 작업을 완료하는 데 스레드 안전 무승부 () 메소드를 제공 
    DEF 그리기를 (자체, draw_amount) :
         #의 잠금
        self.lock.acquire ()
         은 try :
             #의 계정 잔액이 인출의 수보다 큰 
            IF self._balance> = draw_amount :
                 #의 침 법안 
                인쇄 .이 (threading.current_thread () 이름 \
                     + " 인출 성공 침 법안 :! " + STR ( draw_amount)) 
                합니다 (time.sleep 0.001 )
                 # 밸런스 수정 
                self._balance은 - = draw_amount의
                 인쇄 ( " \ T 균형입니다 : " + STR (self._balance))
             다른 :
                인쇄 (. threading.current_thread ()를 이름 \
                     + " 돈을 수집하는 부족 자금을 실패! " )
         최종적 :
             #의 수정이 완료되면 잠금 해제 
            (self.lock.release를)

위의 절차는 RLock 객체를 정의합니다. 실행 과정 진입 직후 RLock 잠금 개체 요청할 때 프로그램 연신 () 방식으로 구현하는 논리를 실행할 때 인출 그릴 () 메소드는, 프로그램은 마지막 로크의 방출을 확보하기 위해 사용 차단할.

self.balance, RLock 개체에서 반드시 1 자물쇠를 수정할 때 동기 로크 같은 프로그램 RLock 객체는 각 스레드가 실행 연신 () 메소드를 시작한다. self._balance에 스레드 완료 수정하면 종료 무승부 () 메서드를 원할 것입니다 때, 다음 RLock 개체의 잠금을 해제. 이 방법은 보안 액세스 논리 "잠금을 해제 → 변경 → 잠금"에 맞춰 완전히이다.

다른 스레드 RLock 객체에 대한 잠금을 얻을 수 없기 때문에 그들이 동시에 수행 그리도록, 연신 () 메소드에 로킹 스레드 RLock 객체 () self._balance 방법이 변경 될 때. 주어진 시간에 동시 스레드가 하나 개의 스레드가 액세스 할 수 공유 리소스에 따라서 보안 스레드를 보장하는 동시에 중요한 지역에서 가장 하나 개의 스레드에서 그렇게 (또한 중요한 부분이라고도 함)의 코드 영역을 수정하는 것이이 의미합니다.

정말 "잠금"이 계정 개체를 관리 할 수있는 잠금 개체를 보장하기 위해, 프로그램이 잠금 계정이 각 개체에이 기록됩니다 해당 (방 등의 잠금을 가지고).

클래스 대신 위의 계정은 돈 무승부 () 메소드를 철회하고 보장하기 위해 잠금 개체를 사용하여 추가하는 그리기 때문에 () 메소드는 스레드 안전하지만 취소 setBalance () 메소드 (피하기 직접 수정 프로그램 self._balance 멤버 변수) 실행 스레드는 단순히 돈을 인출 할 수있는 작업을 수행 할 수있는 계정 개체의 무승부 () 메서드를 호출합니다.

다음 프로그램은 두 개의 인출 및 시작 쓰레드를 생성 :

가져 오기 스레딩
 가져 오기 계정
 #이 돈을 인출 할 수있는 동작을 시뮬레이션하는 함수를 정의 
DEF (계정, draw_amount) 그리기 :
     # 돈을 철회 할 수있는 작업을 수행하는 계정 그리기 개체에 직접 () 메서드 호출을 
    account.draw을 (draw_amount)
 #은 계정의 생성 
ACCT = 계정. 계정 ( " 1234567 " , 1000 )
 #이 돈을 인출 할 수있는 동일한 계정에 두 개의 스레드 시뮬레이션 
threading.Thread의를 (이름 = ' ' = 대상, 인수 = (ACCT 그리기, 800 )). 시작 () 
threading.Thread의 (이름 = ' B ' = 대상 그리기 인수 = (ACCT 800)). (시작)

() 함수의 실행 무승부 스레드 대신 위의 프로그램은 자신의 작업을 달성하기 위해 필요하지만, 직접 돈을 인출 할 수있는 작업을 수행 할 수있는 무승부 계정 () 메소드를 호출하지 않고 돈을 얻을 수 있습니다. 위의 프로그램이 스레드 안전 문제로 이어질되지 않도록 추첨 () 메소드는 이미 RLock 객체가 구현하는 스레드 안전을 사용하기 때문에.

항상도 결과를 볼, 위의 과정을 여러 번 반복 실행합니다.

성공적인 걸릴 돈! 법안을 뱉어 : 800 
    균형은 다음과 같습니다 (200) 
B는 돈이 실패 철회! 부족 자금! 

프로세스 종료 코드 완료 0

 

 

추천

출처www.cnblogs.com/yangzhen-ahujhc/p/12319306.html