32. 단일 조건변수 + Pulse(notify_one) 의 깨어남 유실(생산자/소비자 혼재) (C#)

난이도 상 해설 보기 →

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

결함 코드 · C#
// 시나리오: 게임 서버의 유계(bounded) 작업 큐. 생산자 스레드(수신/타이머)가 작업을 넣고,
// 소비자 워커 스레드들이 꺼내 처리한다. 큐가 가득 차면 생산자가 대기하고, 비어 있으면
// 소비자가 대기한다. 두 대기 모두 하나의 lock 객체(모니터)에서 이뤄진다.
//
// 요구사항:
//   - Enqueue: 가득 차 있으면 자리가 날 때까지 대기했다가 넣는다.
//   - Dequeue: 비어 있으면 항목이 생길 때까지 대기했다가 꺼낸다.
//   - 대기 술어는 while 로 재검사한다(가짜 깨어남 대비).
//   - 생산자·소비자가 여럿이어도 교착 없이 흘러야 한다.
//
// 과제: (A)(B) 지점을 검토하라. 특정 스레드 수·타이밍에서 큐에 자리가/항목이 있는데도
//       대기하던 스레드가 깨어나지 못해 멈출 수 있다. 어떤 상황에서 그런지 밝히고 고쳐라.

using System.Collections.Generic;
using System.Threading;

public sealed class BoundedQueue<T>
{
    private readonly object _gate = new();
    private readonly Queue<T> _q = new();
    private readonly int _cap;

    public BoundedQueue(int capacity) { _cap = capacity; }

    public void Enqueue(T item)
    {
        lock (_gate)
        {
            while (_q.Count == _cap)   // 가득 참 → 대기
                Monitor.Wait(_gate);   // (A)

            _q.Enqueue(item);
            Monitor.Pulse(_gate);      // (B) 대기 중인 스레드 하나를 깨움
        }
    }

    public T Dequeue()
    {
        lock (_gate)
        {
            while (_q.Count == 0)      // 비어 있음 → 대기
                Monitor.Wait(_gate);   // (A)

            T item = _q.Dequeue();
            Monitor.Pulse(_gate);      // (B) 대기 중인 스레드 하나를 깨움
            return item;
        }
    }
}
내 리뷰 · C#
내 답안 · 자동 저장

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