2012-07-04 5 views
2

프레임 워크에서 잠금 메커니즘을 수행해야하는 클래스를 구현해야합니다. 우리는 여러 스레드를 가지고 있으며 0,1,2,3 번으로 번호가 매겨져 있습니다. 우리는 ResourceHandler이라는 정적 클래스를 가지고 있습니다. 요구 사항은 n Lock() 호출이 m Release() 호출에 의해 재실행되어야한다는 것입니다. 여기서 n = [0 ..] 및 m = [0 ..]입니다. 따라서 단일 객체에서 얼마나 많은 잠금이 수행 되더라도 하나의 Release() 호출만으로 모두 잠금을 해제 할 수 있습니다. 객체가 잠겨 있지 않은 경우에도 Release() 호출은 아무 것도 수행하지 않아야합니다. 또한 어떤 스레드에서 어떤 객체가 잠겨 있는지를 알아야합니다.다중 잠금 작업 (스레딩)

public class ResourceHandler 
{ 
    private readonly Dictionary<int, List<object>> _locks = new Dictionary<int, List<object>>(); 

    public static ResourceHandler Instance {/* Singleton */} 

    public virtual void Lock(int threadNumber, object obj) 
    { 
     Monitor.Enter(obj); 

     if (!_locks.ContainsKey(threadNumber)) {_locks.Add(new List<object>());} 
     _locks[threadNumber].Add(obj); 
    } 

    public virtual void Release(int threadNumber, object obj) 
    { 
     // Check whether we have threadN in _lock and skip if not 
     var count = _locks[threadNumber].Count(x => x == obj); 
     _locks[threadNumber].RemoveAll(x => x == obj); 

     for (int i=0; i<count; i++) 
     { 
      Monitor.Exit(obj); 
     } 
    } 

    // ..... 
} 

는 사실 내가 여기에 스레드 안전성 약 걱정 무엇 :

나는이 구현이있다. 저는 실제로 확실하지 않습니다. thread-safe인지 아닌지, 그리고 그것을 고치는 것은 정말 고통 스럽습니다. 작업을 올바르게 수행하고 스레드 안전성을 보장 할 수있는 방법은 무엇입니까?

+0

하나의 스레드가 다른 스레드를 중지 시키거나 threadN 현재 스레드의 수라고 생각합니까? – Kek

+0

threadN은 현재 스레드의 번호입니다. 잠금 및 릴리스는 Monitor.Enter/Exit가 스레드를 잠 그거나 해제해야하지만 많은 잠금에 대한 규칙 (많은 릴리스가 만족되어야합니다)을 수행해야합니다. 또한 개체를 잠글 수 있고 스레드 번호를 알고 있어야합니다 (각 스레드에 대한 정리가 있습니다. 모든 개체가 finally 블록에서 해제되어 응용 프로그램이 중지 될 때 모든 잠금이 해제되도록 할 수 있습니다) – Archeg

+0

threadN을 threadNumber로 변경했습니다. – Archeg

답변

2

귀하의 Lock 방법 잠금을해야 할 수도 있습니다 생각하지만 _locks 사전은에서 임의의 스레드에 액세스 할 수 있습니다 언제든지. 사전에 액세스하기 위해 개인 잠금 객체를 추가 할 수도 있습니다 (LockRelease 메소드 모두에서).

또한 이러한 ResourceHandler를 사용하면 사용 된 모든 객체를 해제하는 것이 나머지 코드 (소비 스레드)의 책임입니다 (일반 lock() 블록은 lock 개체의 범위는 개체가 릴리스됩니다.

== 대신 개체가 잠기는 횟수를 계산할 때 ReferenceEquals을 사용할 수도 있습니다.

+0

자물쇠를 배치하는 것은 문제입니다. 예를 들어, 이미 Monitor.Enter()가 있으므로 교착 상태가 발생하므로 Lock() 메서드 내부에 lock() {}을 배치 할 수 없습니다. 그리고 해제에 대해서 - 그래, 그 이유 중 하나는 모든 잠긴 개체와 그들의 스레드 번호를 계산해야합니다 이유는 – Archeg

+0

당신은 잠금에'모니터'호출을 포함하지 말아야 사전에 대한 액세스. 교착 상태는 한 번에 둘 이상의 객체에서 스레드가 잠글 때 항상 발생하며 동일한 순서로 발생하지 않을 때 발생합니다. –

+0

그런 경우 릴리스 방법과 같은 코드에 문제가 있습니다. 나는 자물쇠를 계산하고, count 변수를 반환하는 코드를 가지고있다. 이 카운트 변수만큼의 릴리스를 수행해야합니다. 사전 사용법을 자물쇠로 감쌀 수 있지만 여전히이 카운트 변수가 안전하지 않습니다. – Archeg

0

IMO 안전을 위해 함수의 기본 세트를 사용해야합니다. 내 생각

http://msdn.microsoft.com/en-us/library/system.threading.mutex.aspx

뮤텍스 유 도움이 될 것입니다.

+0

나는 모든 언덕과이 문제의 추락을 알고 싶다. 모니터를 사용하고 있습니다. 원자 적 조작이 아닙니까? 프로세스 간 잠금이 필요하지 않으므로 모니터가 내 상황에서 뮤텍스와 동일한 작업을 수행해야합니다. – Archeg

+0

예. 모니터가 필요합니다. Profiler에서 수천 개의 스레드가있는 주말에 광범위한 테스트를 실행하십시오. 모든 것을 잘 구현했는지 확인하십시오. 일부 스트레스 테스트. –

1

ConcurrentDictionary을 사용하여이 클래스가 스레드로부터 안전하다는 것을 보장 할 수 있지만 자체 잠금 메커니즘을 개발할 때 발생하는 모든 문제에 도움이되지는 않습니다.

.Net Framework에 이미 포함되어있는 잠금 메카니즘이 여러 개 있습니다. 그 중 하나를 사용해야합니다.

당신이 원하는 것을 얻기 위해 Wait Handles을 포함하여 이것들의 조합을 사용해야 할 것처럼 들립니다.


편집

더주의 깊게 읽은 후, 나는 당신이 목표에 objectEventWaitHandle

+0

내 요구 사항에 맞는 것을 제안 해 줄 수 있습니까? – Archeg

+0

이벤트를 통해이를 수행하는 방법을 아직 모릅니다. 어제 전체를 여기에서 사용하려고했습니다. 상당히 복잡해 보입니다 – Archeg

1

개념 상 위험한 것처럼 보입니다. 이것은 문으로 작동하는 Monitor.EnterMonitor.Exit에 대한 호출이 try/finally 블록으로 캡슐화되어 연속적으로 실행되도록하는 것이 좋습니다. Monitor.ExitMonitor.Enter 전에 호출하면 예외가 발생합니다.

예외가 발생하면 주어진 스레드에 대한 잠금이 취해질 수도 있고 수행되지 않을 수도 있으며, 잠금이 해제되면 잠금이 해제되어 누출 된 잠금이 발생할 수 있습니다. 위의 다른 답변에서 제공하는 옵션 중 하나를 사용하여 권 해드립니다 것입니다. 그러나이 메커니즘을 진행하고자하는 경우, CLR 4.0

public static void Enter (object, ref bool lockTaken); 

lockTaken이 거짓 인 Monitor.Enter 방법으로 다음과 같은 과부하를 추가 경우만 Enter 메서드가 예외를 throw하고 잠금이 수행되지 않습니다. 따라서 boollockTaken을 사용하는 두 가지 방법을 사용하여 무언가를 만들 수 있습니다 (여기 예제는 단일 로커에 대한 것입니다 - 귀하의 스레드에 해당하는 List<bool> 사전이 필요합니다 - 또는 이벤트가 더 나은 Tuple). 당신이 다른 방법 Release

if (lockTaken) 
    Monitor.Exit(locker); 

bool lockTaken = false; 
Monitor.Enter(locker, ref lockTaken); 

같은 것을했을 Lock 당신의 방법에 그래서 난이 도움이되기를 바랍니다.

편집 : 귀하의 문제에 대해 충분히 고맙지 만, 모을 수있는 부분은 Concurrent Collection입니다. 이것들은 완전히 안전합니다. IProducerConsumerCollection<T>ConcurrentBag<T>을 확인하십시오. 이것들은 프레임 워크에 의해 처리되는 모든 thread safter로 원하는 것을 용이하게해야한다. (thread safe collection은 그것이 실행하는 코드가 쓰레드에 안전하다는 것을 의미하지는 않는다.) 그러나 이와 같은 컬렉션을 사용하면 잠금을 사용하는 것보다 훨씬 느려질 수 있습니다.

+0

사전을 열거하고 스레드 끝에서 잠금을 해제하여 모든 잠금을 해제 할 수 있습니다. 실제로 모든 스레드는 최종적으로 차단됩니다. 이 과부하는 아주 멋지게 보였습니다. 사용하려고했지만 여전히 전체 스레드 안전성을 유지하는 방법을 아직 모릅니다. Lock 메서드가 호출 된 횟수를 계산할 필요가 있습니다. 즉, 스레드로부터 안전하지 않은 사전이나 목록을 가지고 있음을 의미합니다. ConcurrentDictionary를 사용하더라도 사전을 열거하고 릴리스를 호출하는 것과 같은 스레드 안전하지 않은 코드가 있습니다. – Archeg

+0

다른 동시 컬렉션을 보려면 작은 편집을 추가했습니다. 위와 같이 각'locker'가'Dictonary/Tuple'에 lockTaken을 포함하고 있다고 가정하면, 튜플에 카운트와 잠금이 취해 지는지 여부를 추가합니다. 이렇게하면 '찍은'잠금이 해제되지 않습니다. 행운을 빕니다... – MoonKnight