我有一个基于 BlockingCollection 的带有生产者消费者模式的简单记录器(代码如下)。
public class Logger
{
public Logger()
{
_messages = new BlockingCollection<LogMessage>(int.MaxValue);
_worker = new Thread(Work) {IsBackground = true};
_worker.Start();
}
~Logger()
{
_messages.CompleteAdding();
_worker.Join(); // Wait for the consumer's thread to finish.
//Some logic on closing log file
}
/// <summary>
/// This is message consumer thread
/// </summary>
private void Work()
{
while (!_messages.IsCompleted)
{
//Try to get data from queue
LogMessage message;
try
{
message = _messages.Take();
}
catch (ObjectDisposedException) { break; } //The BlockingCollection(Of T) has been disposed.
catch(InvalidOperationException){ continue; } //the BlockingCollection(Of T) is empty and the collection has been marked as complete for adding.
//... some simple logic to write 'message'
}
}
}
问题是应用程序并没有立即结束。结束一个应用程序需要 20-40 秒,如果我在中间使用调试器暂停它,我会看到:
1. GC.Finalize 线程设置在 _worker.Join();
2. _worker 线程在 _messages.Take() 上。
我会等待 _messages.Take() 在 _messages.CompleteAdding(); 之后结束。但看起来不是。
这种终结有什么问题,在这种情况下如何更好地终结工作线程?
PS 我可以简单地删除 _worker.Join() 但随后 Work() 可以向关闭的文件写入一些内容。我的意思是,这是并发的不确定情况。
更新
作为概念证明,我已将 ~Logger() 重命名为 Close() 并在某个时候调用它。它立即关闭记录器。所以 _messages.Take() 在 _messages.CompleteAdding() 之后结束,正如在这种情况下所预期的那样。
我在 GC 线程的高优先级中看到的 ~Logger 中 20-40 秒延迟的唯一解释。能不能有别的解释?