BlockingCollection (코드는 아래 참조)을 기반으로 한 생산자 고객 패턴을 사용하는 간단한 로거가 있습니다.BlockingCollection의 IDisposable 대 생산자 소비자
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(); 그러나 그렇지 않은 것처럼 보입니다.
이 마무리에서 어떤 문제가 있으며이 상황에서 작업자 스레드를 더 잘 마무리하는 방법은 무엇입니까?
P. _worker.Join()을 간단히 놓을 수는 있지만 Work()는 닫힌 파일에 무언가를 쓸 수 있습니다. 내 말은, 이것은 동시에 비 결정적인 상황입니다.
나는 ~ 로거()를 닫습니다()와 어떤 점에서 그것을 호출로 변경 한 개념 증명으로
업데이트. 즉시 로거를 닫습니다. 그래서 _messages.Take()는 _messages.CompleteAdding() 바로 다음에 끝날 것입니다.
~ Logger에서 20-40 초 지연에 대한 유일한 설명은 GC 스레드의 높은 우선 순위에서 볼 수 있습니다. 다른 설명이있을 수 있습니까?
예, GC가 공간을 확보 할 필요가 없기 때문에 마무리 도구가 오랫동안 호출되지 않았기 때문에 지연이 발생했습니다. – ricovox
@ricovox 정말 도와 주셔서 감사합니다! 그러나, 제발, 더 \t stackoverflow 질문에 사려 깊어. 귀하의 의견은 묻는 질문과 아무 관련이 없습니다. – MajesticRa