8. IDisposable·파이널라이저와 Dispose 패턴 (결정적 해제와 SuppressFinalize)
난이도 중 해설 보기 →
결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커
(A)(B) 는 주목 위치 힌트다.
결함 코드 · C#
// ============================================================================
// [내부동작 문제] C# - IDisposable·파이널라이저와 Dispose 패턴
// ----------------------------------------------------------------------------
// 주제:
// 결정적 자원 해제(IDisposable/using)와 비결정적 정리(파이널라이저/~Finalizer),
// GC.SuppressFinalize 의 역할, 그리고 "Dispose 를 깜빡했을 때" 무슨 일이 생기는지를
// 예측하고 설명한다.
//
// Handle 은 표준 Dispose 패턴을 흉내 낸다: 파이널라이저와 Dispose 를 모두 가지고,
// Dispose 에서 SuppressFinalize 를 호출한다. 각 경로는 "dispose: X" / "finalizer: X" 를
// 출력한다.
//
// 과제:
// 1) 아래 Main 의 출력(줄 내용과 순서)을 예측하라. 특히 A(using 으로 정리)와
// B(Dispose 없이 버려짐)에 대해 파이널라이저가 각각 실행되는지 답하라.
// 2) Dispose 에서 GC.SuppressFinalize(this) 를 부르는 이유를 설명하라(부르지 않으면?).
// 파이널라이저가 도는 스레드·시점이 왜 비결정적이며, 자원(소켓/파일핸들 등)을
// 파이널라이저에만 의존하면 무엇이 문제인지 설명하라.
// 3) 게임서버에서 소켓/네이티브 핸들을 다룰 때 이 패턴(혹은 SafeHandle)이 왜 중요한지 적어라.
// ※ 먼저 예측을 적은 뒤 answer.md 와 대조할 것.
// ============================================================================
using System;
public sealed class Handle : IDisposable
{
private bool _disposed;
public string Name;
public Handle(string name) { Name = name; }
~Handle()
{
// 파이널라이저: GC 가 수거할 때 파이널라이저 스레드에서 실행
Console.WriteLine($"finalizer: {Name}");
ReleaseUnmanaged();
}
public void Dispose()
{
if (_disposed) return;
ReleaseManaged();
ReleaseUnmanaged();
_disposed = true;
GC.SuppressFinalize(this); // (핵심) 이미 정리했으니 파이널라이저 불필요
Console.WriteLine($"dispose: {Name}");
}
private void ReleaseManaged() { /* 관리 자원 정리 */ }
private void ReleaseUnmanaged() { /* 비관리 자원(핸들 등) 해제 */ }
}
public static class Program
{
public static void Main()
{
using (var a = new Handle("A")) { // 블록 끝에서 Dispose 자동 호출
}
var b = new Handle("B"); // Dispose 를 호출하지 않고
b = null; // 참조만 끊는다
GC.Collect(); // 수거 유도
GC.WaitForPendingFinalizers(); // 파이널라이저 완료까지 대기
Console.WriteLine("done");
}
} 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.