47. 이벤트 루프의 연결당 무제한 드레인 (공정성 붕괴 / 기아)
난이도 상 해설 보기 →
결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커
(A)(B) 는 주목 위치 힌트다.
결함 코드 · C#
// 시나리오: 단일 스레드 이벤트 루프(리액터) 기반 세션 처리 — C# 서버
// - 한 워커 스레드가 준비된(수신 데이터 있는) 모든 연결을 순회하며 메시지를 처리한다.
// - 대부분의 클라이언트는 초당 몇 개의 패킷만 보내지만, 일부(봇/매크로/버그 클라,
// 혹은 대량 전송 구간의 클라)는 매우 빠르게 연속 전송해 수신 큐가 급격히 쌓인다.
// - 같은 루프가 타이머·하트비트·다른 세션 처리도 담당한다(단일 스레드).
//
// 요구사항:
// 1) 한 연결이 아무리 많이 보내도 다른 연결·타이머 처리가 지나치게 지연되면 안 된다.
// 2) 처리량은 연결들 사이에 공평하게 분배되어야 한다(한 연결 독점 금지).
//
// 과제: 특정 연결이 데이터를 폭주시키면 다른 연결들의 응답이 크게 밀리거나 굶는다.
// (A),(B) 지점을 근거로 원인과 수정안을 설명하라.
using System;
using System.Collections.Generic;
public sealed class Message { public int Seq; }
public sealed class Connection
{
public int Id;
public readonly Queue<Message> Inbox = new Queue<Message>(); // 소켓 수신 큐(모의)
public bool TryReceive(out Message m)
{
if (Inbox.Count > 0) { m = Inbox.Dequeue(); return true; }
m = null; return false;
}
}
public sealed class Reactor
{
private readonly List<Connection> _conns = new List<Connection>();
public Action<Connection, Message> Handler = (c, m) => { };
public void Add(Connection c) => _conns.Add(c);
// 이벤트 루프 1회(tick). 준비된 연결을 순회한다.
public void Tick()
{
foreach (var conn in _conns) // (A)
{
while (conn.TryReceive(out var msg)) // (B)
{
Handler(conn, msg);
}
}
// 이후: 타이머/하트비트/기타 세션 유지 작업 (Tick 끝에서 수행)
}
} 결함 코드 · C++
// 시나리오: epoll 기반 단일 스레드 이벤트 루프 — C++ 서버
// - 한 워커가 준비된 소켓들을 순회하며, 각 소켓에서 데이터를 읽어 처리한다.
// - 대부분 클라이언트는 소량 전송하지만 일부는 폭주 전송(firehose)한다.
// - 같은 루프가 타이머/하트비트/다른 세션도 담당한다(단일 스레드).
//
// 요구사항:
// 1) 한 연결이 많이 보내도 다른 연결·타이머가 과도하게 지연되면 안 된다.
// 2) 처리량은 연결 간 공평해야 한다(한 연결 독점 금지).
//
// 과제: 한 연결이 데이터를 폭주시키면 다른 연결이 굶는다.
// (A),(B) 지점을 근거로 원인·수정안을 설명하라. (컴파일 가능해야 함)
#include <cstdio>
#include <deque>
#include <functional>
#include <vector>
struct Message { int seq; };
struct Connection {
int id = 0;
std::deque<Message> inbox; // 소켓 수신 큐(모의: recv() 반복을 흉내)
// recv() 성공 시 true 와 메시지 반환
bool TryReceive(Message& out) {
if (inbox.empty()) return false;
out = inbox.front(); inbox.pop_front(); return true;
}
};
class Reactor {
public:
std::function<void(Connection&, const Message&)> handler = [](Connection&, const Message&){};
void Add(Connection* c) { conns_.push_back(c); }
void Tick() {
for (Connection* conn : conns_) { // (A)
Message m;
while (conn->TryReceive(m)) { // (B) EAGAIN(=빈 큐)까지 전부 드레인
handler(*conn, m);
}
}
// 이후: 타이머/하트비트 처리
}
private:
std::vector<Connection*> conns_;
};
int main() {
Reactor r;
Connection a, b; a.id = 1; b.id = 2;
a.inbox.push_back({1}); b.inbox.push_back({1});
r.Add(&a); r.Add(&b);
r.handler = [](Connection& c, const Message& m){ std::printf("conn=%d seq=%d\n", c.id, m.seq); };
r.Tick();
return 0;
} 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.
내 리뷰 · C++
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.