38. 틱 이중 버퍼 스왑의 동시성 결함 (가시성·찢긴 읽기, C#)

난이도 최상 해설 보기 →

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

결함 코드 · C#
// 시나리오: 게임 서버 틱 루프의 월드 스냅샷 이중 버퍼링 (C#)
//
// 시뮬레이션 스레드는 매 틱 world state 를 "쓰기 버퍼"에 갱신하고 틱 끝에서 스왑한다.
// 다수의 네트워크 워커 스레드는 "읽기 버퍼"에서 스냅샷을 읽어 브로드캐스트한다.
// 목표는 워커가 락 없이 일관된(찢기지 않은) 최신 스냅샷을 읽게 하는 것이다.
//
// 요구사항:
//  - 워커는 완결된(쓰기 완료된) 버퍼만 읽는다.
//  - 시뮬레이션이 채우는 도중의 버퍼를 워커가 읽어선 안 된다.
//  - 스왑 후 워커는 곧 새 버퍼를 본다.
//
// 아래 구현을 검토하라. 표시된 지점 (A)(B)(C).

using System.Collections.Generic;

public struct EntityState { public ulong Id; public float X, Y, Hp; }

public sealed class WorldSnapshot
{
    public List<EntityState> Entities = new();
    public ulong Tick;
}

public sealed class DoubleBufferedWorld
{
    private readonly WorldSnapshot[] _buffers = { new(), new() };
    private int _readIndex = 0;   // 0 또는 1

    // 시뮬레이션 스레드 전용
    public void CommitTick(WorldSnapshot next)
    {
        int writeIndex = 1 - _readIndex;     // (A) 쓰기 버퍼 선택
        _buffers[writeIndex] = next;         // 새 상태 기록
        _readIndex = writeIndex;             // (B) 스왑
    }

    // 다수 네트워크 워커
    public WorldSnapshot CurrentForRead()
    {
        return _buffers[_readIndex];         // (C) 현재 읽기 버퍼
    }
}
내 리뷰 · C#
내 답안 · 자동 저장

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