Предотвращение доступа к параллельной очереди при перестроении

У меня есть ConcurrentQueue, который содержит объект с некоторыми вспомогательными методами для постановки в очередь и удаления из очереди. Необходимо удалить элементы из очереди, которые соответствуют некоторым критериям, а затем перестроить очередь, поскольку они могут не находиться в первом исключенном из очереди элементе.

Как я могу сделать это безопасно?

ConcurrentQueue должен быть потокобезопасным, но я также хочу предотвратить добавление/удаление элементов из очереди, когда происходит удаление и перестроение.

Я пытаюсь это сделать, но не уверен, что это правильно.

public class EmailRepository
{
    private readonly object _lockObj _lockObj = new object();
    private ConcurrentQueue<EmailMessage> _emailMessages = new ConcurrentQueue<EmailMessage>();

    public ConcurrentQueue<EmailMessage> EmailMessages => _emailMessages;

    public void AddEmailMessage(string subject, string body)
    {
        _emailMessages.Enqueue(new EmailMessage(subject, body));
    }

    public void AddEmailMessage(EmailMessage message)
    {
        _emailMessages.Enqueue(message);
    }

    public bool RemoveEmailMessage(out EmailMessage message)
    {
        return _emailMessages.TryDequeue(out message);
    }

    public void RemoveSiteEmailsAndRebuildQueue(int key)
    {
        for (int i = 0; i < _emailMessages.Count; i++)
        {
            RemoveEmailMessage(out EmailMessage message);
            if (message.KeyValue.Equals(key))
            {
                continue;
            }

            AddEmailMessage(message);
        }
    }
}

Я раскрываю _emailMessages, но на самом деле ничто не изменяет коллекцию, если они не вызывают соответствующие методы.

Я думаю, что мне следует что-то добавить в логику для RemoveSiteEmailsAndRebuildQueue, чтобы гарантировать, что ничего не будет поставлено в очередь или удалено из очереди, пока этот метод не будет выполнен, но я не уверен, что это за что-то.


person Jimenemex    schedule 24.03.2021    source источник
comment
выглядит так, что добавление оператора блокировки (docs. microsoft.com/en-us/dotnet/csharp/language-reference/) для всех вспомогательных функций будет достаточно.   -  person Serg    schedule 24.03.2021


Ответы (1)


Вы даже можете не использовать ConcurrentQueue и использовать простой объект блокировки, например:

public class EmailRepository
{
    private readonly object _lockObj = new object();
    private Queue<EmailMessage> _emailMessages = new Queue<EmailMessage>();

    public Queue<EmailMessage> EmailMessages => _emailMessages;

    public void AddEmailMessage(string subject, string body)
    {
        lock (_lockObj)
        {
            _emailMessages.Enqueue(new EmailMessage(subject, body));
        }
    }

    public void AddEmailMessage(EmailMessage message)
    {
        lock (_lockObj)
        {
            AddWithoutLock(message);
        }
    }

    private void AddWithoutLock(EmailMessage message)
    {
        _emailMessages.Enqueue(message);

    }

    public bool RemoveEmailMessage(out EmailMessage message)
    {
        lock (_lockObj)
        {
            return _emailMessages.TryDequeue(out message);
        }
    }

    public void RemoveSiteEmailsAndRebuildQueue(int key)
    {
        lock (_lockObj)
        {
            for (int i = 0; i < _emailMessages.Count; i++)
            {
                RemoveEmailMessage(out EmailMessage message);
                if (message.KeyValue.Equals(key))
                {
                    continue;
                }

                AddWithoutLock(message);
            }
        }
    }
}
person Amir    schedule 24.03.2021