Pergunta

A seguir # classe C é usado em um ambiente de vários segmentos. Tirei muito do código real. O problema ocorre quando chamando MethodA e MethodB quase simultaneamente. O fim do bloqueio na propriedade IsDepleted não resolve o problema. Remoção de bloqueio (WaitingQueue) das resolve propriedade IsDepleted o impasse, mas esta solução causa um problema quando outro segmento adiciona / remove um item da WaitingQueue entre o WaitingQueue.Count == 0 e Processing.Count == 0 declarações.

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
    }
}
Foi útil?

Solução

Depende se você quer uma solução rápida ou uma correção rigorosa.

Uma solução rápida seria apenas para usar um objeto de bloqueio em todos os casos.

por exemplo. private readonly object _lock = new object();

E depois é só bloquear isso. No entanto, dependendo da sua situação, que pode afetar o desempenho mais do que você pode aceitar.

i. seu código se tornaria o seguinte:

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
    }
}

Outras dicas

Leve o bloqueio de Processamento no método A e o bloqueio WaitingQueue no método B (em outras palavras, fazer parecer que o primeiro bloco de código). Dessa forma, você sempre tomar os bloqueios na mesma ordem e você nunca vai impasse.

simplificar o seu código e usar apenas um único objeto para bloquear. Você também pode substituir seus bloqueios com:

Monitor.TryEnter (Processamento, 1000)

isso vai lhe dar um segundo tempo 1. Então, basicamente:

        if (Monitor.TryEnter(Processing, 1000))
        {
            try
            {
                //do x
            }
            finally
            {
                Monitor.Exit(Processing);
            }
        }

Agora você não vai parar o impasse, mas você pode lidar com o caso em que você não obter um bloqueio.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top