29. 침투적 참조 카운트의 비원자성 (버퍼 풀의 이중 반납·반납 후 사용)

난이도 최상 해설 보기 →

결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커 (A)(B) 는 주목 위치 힌트다.

결함 코드 · C#
// 시나리오: 풀에서 빌린 네트워크 송수신 버퍼를 여러 스레드(수신 IO 스레드, 처리 워커, 송신 스레드)가
// 동시에 공유한다. 수명 관리를 위해 버퍼에 "침투적 참조 카운트"를 둔다.
// 참조를 늘릴 때 AddRef, 다 쓰면 Release 를 호출하고, 마지막 참조가 사라지면 버퍼를 풀로 반납한다.
//
// 요구사항:
//  - 마지막 Release 에서만 "정확히 한 번" 풀로 반납돼야 한다(이중 반납·미반납 금지).
//  - 반납된 뒤에는 아무도 그 버퍼를 건드리면 안 된다(반납 후 사용 금지).
//
// 과제: (A)(B) 를 중심으로, 여러 스레드가 동시에 AddRef/Release 할 때
//       참조 카운트가 어떻게 틀어질 수 있는지와 그로 인한 결과, 그리고 수정안을 제시하라.

public sealed class PooledBuffer
{
    private int _refs = 1;                  // 생성 시 1
    public byte[] Data { get; } = new byte[2048];

    private readonly BufferPool _pool;
    public PooledBuffer(BufferPool pool) => _pool = pool;

    public void AddRef()
    {
        _refs++;                            // (A)
    }

    public void Release()
    {
        if (--_refs == 0)                   // (B)
            _pool.Return(this);            // 풀로 반납
    }
}

public sealed class BufferPool
{
    // 반납된 버퍼를 재사용 목록에 넣는다(상세 생략).
    public void Return(PooledBuffer buf) { /* ... 재사용 큐에 push ... */ }
    public PooledBuffer Rent() => new PooledBuffer(this);
}
내 리뷰 · C#
내 답안 · 자동 저장

작성 후 위 해설 보기에서 모범 해설과 대조하세요.