문제

다음 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);
            }
        }

이제 교착 상태를 중지할 수는 없지만 잠금을 얻지 못한 경우를 처리할 수 있습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top