다음 C# 코드에서 교착 상태를 방지하는 방법은 무엇입니까?
-
22-07-2019 - |
문제
다음 C# 클래스는 다중 스레드 환경에서 사용됩니다.실제 코드를 많이 제거했습니다.MethodA와 MethodB를 거의 동시에 호출할 때 문제가 발생합니다.IsDepleted 속성의 잠금 순서로는 문제가 해결되지 않습니다.IsDepleted 속성에서 잠금(WaitingQueue)을 제거하면 교착 상태가 해결되지만 이 해결 방법은 다른 스레드가 WaitingQueue.Count == 0 및 Process.Count == 0 문 사이의 WaitingQueue에서 항목을 추가/제거할 때 문제를 발생시킵니다.
using System.Collections.Generic;
class Example
{
bool IsDepleted
{
get
{
lock (Processing)
{
lock (WaitingQueue)
{
return WaitingQueue.Count == 0
&& Processing.Count == 0;
}
}
}
}
private readonly List<object> Processing = new List<object>();
private readonly Queue<object> WaitingQueue = new Queue<object>();
public void MethodA(object item)
{
lock (WaitingQueue)
{
if (WaitingQueue.Count > 0)
{
if (StartItem(WaitingQueue.Peek()))
{
WaitingQueue.Dequeue();
}
}
}
}
public void MethodB(object identifier)
{
lock (Processing)
{
Processing.Remove(identifier);
if (!IsDepleted)
{
return;
}
}
//Do something...
}
bool StartItem(object item)
{
//Do something and return a value
}
}
해결책
빠른 수정 또는 엄격한 수정을 원하는지 여부에 따라 다릅니다.
빠른 수정은 모든 경우에 하나의 잠금 객체를 사용하는 것입니다.
예를 들어 private readonly object _lock = new object();
그리고 그냥 잠그십시오. 그러나 상황에 따라 수용 할 수있는 것보다 성능에 더 많은 영향을 줄 수 있습니다.
즉, 코드는 다음과 같습니다.
using System.Collections.Generic;
class Example
{
private readonly object _lock = new object();
bool IsDepleted
{
get
{
lock (_lock)
{
return WaitingQueue.Count == 0
&& Processing.Count == 0;
}
}
}
private readonly List<object> Processing = new List<object>();
private readonly Queue<object> WaitingQueue = new Queue<object>();
public void MethodA(object item)
{
lock (_lock)
{
if (WaitingQueue.Count > 0)
{
if (StartItem(WaitingQueue.Peek()))
{
WaitingQueue.Dequeue();
}
}
}
}
public void MethodB(object identifier)
{
lock (_lock)
{
Processing.Remove(identifier);
if (!IsDepleted)
{
return;
}
}
//Do something...
}
bool StartItem(object item)
{
//Do something and return a value
}
}
다른 팁
메소드 A와 메소드 B의 대기 중 잠금 장치에서 처리 잠금을 취하십시오 (즉, 코드의 첫 번째 블록처럼 보이게하십시오). 그렇게하면 항상 같은 순서로 자물쇠를 가져 가면 교착 상태가 아닙니다.
코드를 단순화하고 단일 개체만 사용하여 잠그세요.자물쇠를 다음으로 교체할 수도 있습니다.
Monitor.TryEnter(처리 중,1000)
그러면 1초의 시간 초과가 발생합니다.그래서 본질적으로:
if (Monitor.TryEnter(Processing, 1000))
{
try
{
//do x
}
finally
{
Monitor.Exit(Processing);
}
}
이제 교착 상태를 중지할 수는 없지만 잠금을 얻지 못한 경우를 처리할 수 있습니다.