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++
// 시나리오: 게임 서버 틱 루프의 월드 스냅샷 이중 버퍼링 (C++)
//
// 시뮬레이션 스레드는 매 틱 world state 를 "쓰기 버퍼"에 갱신하고, 틱 끝에서 버퍼를 스왑한다.
// 다수의 네트워크 워커 스레드는 "읽기 버퍼"에서 스냅샷을 읽어 각 클라이언트에 브로드캐스트한다.
// 목표는 워커가 락 없이 일관된(찢기지 않은) 최신 스냅샷을 읽게 하는 것이다.
//
// 요구사항:
// - 워커는 항상 완결된(쓰기 완료된) 버퍼만 읽는다.
// - 시뮬레이션이 채우는 도중의 버퍼를 워커가 읽어선 안 된다.
// - 스왑 후 워커는 곧 새 버퍼를 보게 된다.
//
// 아래 구현을 검토하라. 표시된 지점 (A)(B)(C).
#include <array>
#include <cstdint>
#include <vector>
struct EntityState { std::uint64_t id; float x, y, hp; };
struct WorldSnapshot { std::vector<EntityState> entities; std::uint64_t tick; };
class DoubleBufferedWorld {
public:
DoubleBufferedWorld() : readIndex_(0) {}
// 시뮬레이션 스레드 전용: 쓰기 버퍼를 채우고 스왑한다.
void CommitTick(const WorldSnapshot& next) {
int writeIndex = 1 - readIndex_; // (A) 쓰기 버퍼 선택
buffers_[writeIndex] = next; // 새 상태 기록
readIndex_ = writeIndex; // (B) 스왑: 읽기 버퍼를 방금 쓴 것으로
}
// 다수 네트워크 워커: 읽기 버퍼에서 스냅샷을 읽는다.
const WorldSnapshot& CurrentForRead() const {
return buffers_[readIndex_]; // (C) 현재 읽기 버퍼
}
private:
std::array<WorldSnapshot, 2> buffers_;
int readIndex_; // 0 또는 1
}; 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.
내 리뷰 · C++
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.