哪个是发布使用BlockingCollection的线程的最佳方法?

时间:2014-08-26 08:51:32

标签: c# .net multithreading

我有一个使用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 Thread将永远不会结束。那是一种内存泄漏。我对吗?以及如何克服这个问题?

1 个答案:

答案 0 :(得分:0)

您可以尝试仅保留工作线程中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);
    }
}