2016-06-03 5 views
4

전에 물어 보면 용서해주세요. 나는 주위를 둘러 보았지만, 웹 검색을 통해 적절한 단어를 찾을 수 없다는 느낌을받습니다.값 기반 스레드 잠금

저는 파이썬에서 멀티 스레드 응용 프로그램을 사용하고 있습니다. 특정 코드 블록을 잠글 수 있지만 특정 조건의 다른 스레드에만 잠금을 설정하려고합니다. 예를 들어 보겠습니다. thread_a, thread_bthread_c 세 개의 스레드가 있습니다. 각 스레드는 언제든지 foo 함수를 통해 실행될 수 있습니다. bar의 두 스레드가 동시에 같아서 Code block ALPHA에 액세스 할 수 없도록하고 싶습니다. 그러나, 나는 bar 값이 다른 스레드를 차단하고 싶지 않습니다. 이 경우 thread_abar == "cat"이 있고 처음으로 (3) 행이 있다고 가정 해 보겠습니다. thread_a 히트 라인 (5) 전에는 thread_b, bar == "cat" 히트 라인 (3)으로 가정 해 봅시다. 기다리는 것은 thread_b입니다. 그러나 thread_c이 표시되면 bar == "dog"과 함께 계속 진행할 수 있기를 바랍니다. 또 다른 참고로

(1) def foo(bar): 
(2)  
(3)  lock(bar) 
(4)  # Code block ALPHA (two threads with equivalent bar should not be in here) 
(5)  unlock(bar) 

bar에 사용할 수있는 값은 완전히 예측할 수 있지만 충돌의 매우 높은 기회입니다.

도움 주셔서 감사합니다. 내가보고 있어요 도서관은 업데이트 날짜 python threading library

+0

'bar'가 유한입니까? 그런 다음 원하는만큼 많은 뮤텍스를 설정 한 다음 스레드에있는 'bar'값에 해당하는 뮤텍스를 잠글 수 있습니다. – numeral

+0

@numeral, 좋은 질문입니다. 막대는 가능한 모든 경우에 대해 뮤텍스를 합리적으로 할당 할 수 있다는 점에서 유한합니다. 이를 반영하기 위해 질문을 업데이트 할 것입니다. –

+0

그런 경우에 내가 생각할 수있는 유일한 것은 당신이 무엇을 'bar'로 추적하고 동적으로 잠글 수 있는지를 추적해야한다는 것입니다. 내가 생각하는 어떤 것이라도 공유 메모리 모델이 있습니다. 즉, 모든 동적 잠금을 잠글 수있는 마스터 잠금이 있어야 함을 의미합니다. 즉, 모든 스레드를 잠그는 뮤텍스를 하나만 가질 수 있습니다. 죄송합니다 도움이되지 못했습니다. :/ – numeral

답변

3

입니다

좋은 소식 : 나는 당신이 내가 함께 자갈길 다소 원유 테스트 베드를 통해 내 원래의 대답을 사용하여 발생한 release_lock 문제를 재현하고 문제를 해결 할 수 있었다 (당신이 제안한 것처럼) 계산 메커니즘을 사용해서 - 적어도 필자가 테스트 장치로 말할 수있는 한 멀리.

이제 두 개의 개별 공유 사전이 사용됩니다. 하나는 이전과 같이 각 잠금과 관련된 "이름"또는 값을 추적하는 것이고 다른 하나는 주어진 시간에 각 스레드를 사용하는 스레드 수를 추적하는 것입니다.

앞에서와 같이 잠금 이름은 사전의 키로 사용할 수 있도록 해시 값이 있어야합니다.

import threading 

namespace_lock = threading.Lock() 
namespace = {} 
counters = {} 

def aquire_lock(value): 
    with namespace_lock: 
     if value in namespace: 
      counters[value] += 1 
     else: 
      namespace[value] = threading.Lock() 
      counters[value] = 1 

    namespace[value].acquire() 

def release_lock(value): 
    with namespace_lock: 
     if counters[value] == 1: 
      del counters[value] 
      lock = namespace.pop(value) 
     else: 
      counters[value] -= 1 
      lock = namespace[value] 

    lock.release() 

# sample usage  
def foo(bar): 
    aquire_lock(bar) 
    # Code block ALPHA (two threads with equivalent bar should not be in here) 
    release_lock(bar) 
+0

작성해 주셔서 감사합니다. 나는 그것을 시험해보고 다시보고 할 것이다. 그것은 유망 해 보입니다. –

+0

나는 어떤 테스트도하기 전에 이것에 감명 받았다. 내가 발견 한 유일한 문제는 값이 네임 스페이스에서 조기에 팝핑 될 수 있다는 것이다. 두 개의 쓰레드가 같은 값을 얻으려고 할 때, 첫 번째 쓰레드에 의해'release_lock'이 호출되면, 그 값을 팝하고, 두번째 쓰레드는'namespace [value] .acquire()'를 거칩니다. 두번째 쓰레드가'release_lock'을 호출하면, 그 값은 더 이상'namespace'에 없으므로 pop 될 수 없습니다. 나는 그것이 고쳐야하는지 알려주는 계산 메커니즘을 추가하는 것이 모두 해결 될 것이라고 생각합니다. 전체적으로 굉장한 답변! –

2

는 스레드가 입력하거나 중요 섹션을 종료하고 bar의 각 값에 대해 별도의 조건 변수를 사용하려고 할 때마다 획득 한 잠금을 보유하고 있습니다. 다음은 아마도 적은 조건 변수를 만들 수 있도록 최적화하지만,이 게시물에 대한 이렇게하면 조기 최적화 같은 느낌 수 :

with critical(bar): 
    # Critical section. 
: 다음

import collections 
import contextlib 
import threading 

lock = threading.Lock() 

wait_tracker = collections.defaultdict(lambda: (False, 0, threading.Condition(lock))) 

@contextlib.contextmanager 
def critical(bar): 
    with lock: 
     busy, waiters, condition = wait_tracker[bar] 
     if busy: 
      # Someone with the same bar value is in the critical section. 

      # Record that we're waiting. 
      waiters += 1 
      wait_tracker[bar] = busy, waiters, condition 

      # Wait for our turn. 
      while wait_tracker[bar][0]: 
       condition.wait() 

      # Record that we're not waiting any more. 
      busy, waiters, condition = wait_tracker[bar] 
      waiters -= 1 

     # Record that we're entering the critical section. 
     busy = True 
     wait_tracker[bar] = busy, waiters, condition 
    try: 
     # Critical section runs here. 
     yield 
    finally: 
     with lock: 
      # Record that we're out of the critical section. 
      busy, waiters, condition = wait_tracker[bar] 
      busy = False 
      if waiters: 
       # Someone was waiting for us. Tell them it's their turn now. 
       wait_tracker[bar] = busy, waiters, condition 
       condition.notify() 
      else: 
       # No one was waiting for us. Clean up a bit so the wait_tracker 
       # doesn't grow forever. 
       del wait_tracker[bar] 

임계 영역을 입력하고자하는 각 스레드 다음을 수행

이 코드는 테스트되지 않았으며 병렬 처리는 특히 잠금 및 공유 메모리 병렬 처리가 어렵습니다. 나는 그것이 효과가 있다는 것을 보장하지 않습니다.

+0

내가 기회를 얻었을 때, 나는 이것을 자세히 살펴볼 것이다. –