34. ArrayPool<T> · Memory<T> · IMemoryOwner<T> 버퍼 풀링의 내부 동작
난이도 중 해설 보기 →
결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커
(A)(B) 는 주목 위치 힌트다.
결함 코드 · C#
// 주제: ArrayPool<T> · Memory<T> · IMemoryOwner<T> 버퍼 풀링의 내부 동작
//
// 게임 서버는 패킷 수신/직렬화에서 초당 수만 개의 임시 버퍼를 만든다. GC 압박을
// 줄이려 ArrayPool<T> 로 배열을 대여/반납한다. 그런데 풀의 계약을 오해하면
// 미묘한 버그가 생긴다.
//
// 과제:
// 1) 아래 Rent 호출들에서 buffer.Length 는 각각 요청한 크기와 같을까?
// ArrayPool 이 배열을 어떻게 버킷팅해서 관리하는지 설명하라.
// 2) ProcessPacket 은 rent한 배열을 어떻게 쓰고 반납하는가? 이 코드에 숨은
// 세 가지 함정(정확한 길이 사용, 반납 후 사용, 민감 데이터 클리어)을 짚어라.
// 3) Span<T> 대신 Memory<T>/IMemoryOwner<T> 가 필요한 경우는 언제인가?
// (특히 async 경계를 넘길 때) 이유를 설명하라.
//
// (동작을 예측한 뒤 설명은 answer.md 를 참조하라. 실행 환경이 있으면 확인하라.)
using System;
using System.Buffers;
using System.Threading.Tasks;
public static class PoolDemo
{
public static void ShowRentSizes()
{
var pool = ArrayPool<byte>.Shared;
foreach (int req in new[] { 100, 1000, 4096, 5000 })
{
byte[] buf = pool.Rent(req);
Console.WriteLine($"requested {req} -> buffer.Length {buf.Length}");
pool.Return(buf);
}
}
// 수신 소켓에서 payloadLen 바이트를 읽어 처리한다.
public static void ProcessPacket(int payloadLen)
{
byte[] buf = ArrayPool<byte>.Shared.Rent(payloadLen);
FillFromSocket(buf, payloadLen);
// 체크섬 계산 (전체 배열을 순회한다)
long sum = 0;
for (int i = 0; i < buf.Length; i++) // (A)
sum += buf[i];
ArrayPool<byte>.Shared.Return(buf); // (B)
Console.WriteLine($"checksum={sum}, first={buf[0]}"); // (C)
}
private static void FillFromSocket(byte[] dst, int n) { for (int i = 0; i < n; i++) dst[i] = 1; }
} 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.