Какой лучший способ освободить поток, который использует BlockingCollection?

1

У меня есть класс, который использует BlockingCollection следующим образом:

public class Logger : IDisposable
    {
        private BlockingCollection<LogMessage> _messages = null;
        private Thread _worker = null;
        private bool _started = false;

        public void Start() 
        {
            if (_started) return;
            //Some logic to open log file
            OpenLogFile();      
            _messages = new BlockingCollection<LogMessage>();  //int.MaxValue is the default upper-bound
            _worker = new Thread(Work) { IsBackground = true };
            _worker.Start();
            _started = true;
        }

        public void Stop()
        {   
            if (!_started) return;

            // prohibit adding new messages to the queue, 
            // and cause TryTake to return false when the queue becomes empty.
            _messages.CompleteAdding();

            // Wait for the consumer thread to finish.
            _worker.Join();  

            //Dispose managed resources
            _worker.Dispose();
            _messages.Dispose();

            //Some logic to close log file
            CloseLogFile(); 

            _started = false;
        }

        /// <summary>
        /// Implements IDiposable 
        /// In this case, it is simply an alias for Stop()
        /// </summary>
        void IDisposable.Dispose() 
        {
            Stop();
        }

        /// <summary>
        /// This is message consumer thread
        /// </summary>
        private void Work()
        {
            LogMessage message;
            //Try to get data from queue
            while(_messages.TryTake(out message, Timeout.Infinite))
                WriteLogMessage(message); //... some simple logic to write 'message'
        }
    }

Я создаю новый экземпляр класса Logger, вызываю его метод Start(). Затем, если я забуду вызвать метод Dispose, когда экземпляр больше не ссылается, то Worker Thread никогда не закончится. Это своего рода утечка памяти. Я прав? и как преодолеть это?

Теги:
multithreading

1 ответ

0

Вы можете попытаться сохранить только слабую ссылку на BlockingCollection в рабочем потоке и не ссылаться на объект, ссылающийся на BlockingCollection. Я сделал статический метод, чтобы убедиться, что мы не ссылаемся на this экземпляр Logger.

Таким образом, сбор может быть завершен/собран, когда он больше не ссылается. Я не уверен, что это сработает, вы должны попробовать. Это зависит от того, поддерживает ли TryTake коллекцию или нет. Он может не работать в отладке, поскольку GC ведет себя по-разному, поэтому попробуйте в выпуске без отладчика.

public class Logger : IDisposable
{
    private BlockingCollection<LogMessage> _messages = null;
    private Thread _worker = null;
    private bool _started = false;

    public void Start() 
    {
        if (_started) return;
        //Some logic to open log file
        OpenLogFile();      
        _messages = new BlockingCollection<LogMessage>();  //int.MaxValue is the default upper-bound
        _worker = new Thread(Work) { IsBackground = true };
        _worker.Start(new WeakReference<BlockingCollection<LogMessage>>(_messages));
        _started = true;
    }

    public void Stop()
    {   
        if (!_started) return;

        // prohibit adding new messages to the queue, 
        // and cause TryTake to return false when the queue becomes empty.
        _messages.CompleteAdding();

        // Wait for the consumer thread to finish.
        _worker.Join();  

        //Dispose managed resources
        _worker.Dispose();
        _messages.Dispose();

        //Some logic to close log file
        CloseLogFile(); 

        _started = false;
    }

    /// <summary>
    /// Implements IDiposable 
    /// In this case, it is simply an alias for Stop()
    /// </summary>
    void IDisposable.Dispose() 
    {
        Stop();
    }

    /// <summary>
    /// This is message consumer thread
    /// </summary>
    private static void Work(object state)
    {
        LogMessage message;
        //Try to get data from queue
        do
        {
            BlockingCollection<LogMessage> messages;
            if (((WeakReference<BlockingCollection<LogMessage>>)state).TryGetTarget(out messages)
                && messages.TryTake(out message, Timeout.Infinite))
            {
                WriteLogMessage(message); //... some simple logic to write 'message'
                continue;
            }
        } while (false);
    }
}
  • 0
    Я думаю, что это не работает, потому что messages.TryTake () будет блокироваться навсегда, если не вызывается метод CompleteAdding () объекта BlockingCollection. Я могу использовать определенный тайм-аут для TryTake (), но я заставлю цикл вращаться снова и снова
  • 0
    Я думаю, что когда BlockingCollection будет завершен, он «разблокирует» TryTake, но я не знаю, может ли это действительно произойти, пока вы вызываете TryTake ... Вы должны попробовать это.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню