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++
// 시나리오: 게임 서버의 유계(bounded) 작업 큐(C++). 생산자 스레드(수신/타이머)가 작업을
// 넣고, 소비자 워커 스레드들이 꺼내 처리한다. 큐가 가득 차면 생산자가, 비어 있으면
// 소비자가 대기한다. 두 대기 모두 하나의 조건변수(cv_)에서 이뤄진다.
//
// 요구사항:
// - push: 가득 차 있으면 자리가 날 때까지 대기했다가 넣는다.
// - pop : 비어 있으면 항목이 생길 때까지 대기했다가 꺼낸다.
// - 대기 술어는 predicate 형 wait 로 재검사한다(가짜 깨어남 대비).
// - 생산자·소비자가 여럿이어도 교착 없이 흘러야 한다.
//
// 과제: (A)(B) 지점을 검토하라. 특정 스레드 수·타이밍에서 큐에 자리가/항목이 있는데도
// 대기하던 스레드가 깨어나지 못해 멈출 수 있다. 어떤 상황에서 그런지 밝히고 고쳐라.
#include <cstddef>
#include <queue>
#include <mutex>
#include <condition_variable>
template <class T>
class BoundedQueue {
public:
explicit BoundedQueue(size_t cap) : cap_(cap) {}
void push(T item) {
std::unique_lock<std::mutex> lk(m_);
cv_.wait(lk, [this] { return q_.size() < cap_; }); // (A) 가득 참 아니면 진행
q_.push(std::move(item));
cv_.notify_one(); // (B) 대기자 하나 깨움
}
T pop() {
std::unique_lock<std::mutex> lk(m_);
cv_.wait(lk, [this] { return !q_.empty(); }); // (A) 비어 있지 않으면 진행
T item = std::move(q_.front());
q_.pop();
cv_.notify_one(); // (B) 대기자 하나 깨움
return item;
}
private:
size_t cap_;
std::queue<T> q_;
std::mutex m_;
std::condition_variable cv_;
}; 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.
내 리뷰 · C++
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.