这是释放使用BlockingCollection的线程的最佳方式

本文关键字:线程 最佳 方式 BlockingCollection 释放 | 更新日期: 2023-09-27 18:19:28

我有一个使用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's 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线程将永远不会结束。这是一种内存泄漏。我说得对吗?以及如何克服这一点?

这是释放使用BlockingCollection的线程的最佳方式

您可以尝试在工作线程中只保留对BlockingCollection的弱引用,而不引用引用BlockingCollection的对象。我制作了一个静态方法来确保我们不引用Logger实例this

这样,当集合不再被引用时,就可以最终确定/收集该集合。我不确定它是否能起作用,你必须尝试,这取决于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's 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);
    }
}