39. 낙관적 스냅샷(seqlock)의 잘못된 구현: 찢긴 읽기 (C#)
난이도 최상 해설 보기 →
결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커
(A)(B) 는 주목 위치 힌트다.
결함 코드 · C#
// 시나리오: 게임 서버 — 자주 읽히고 가끔 쓰이는 플레이어 트랜스폼 스냅샷
//
// 한 플레이어의 위치/속도(트랜스폼)는 물리 스레드 1개가 갱신하고, 다수의
// 스레드(AOI 브로드캐스트, 충돌 판정, 스킬 판정)가 매우 빈번히 읽는다.
// 읽기가 압도적으로 많아 뮤텍스 대신 "버전 카운터 기반 낙관적 읽기"로 설계했다.
//
// 요구사항:
// - 읽는 쪽은 항상 "한 시점의 일관된 스냅샷"(같은 갱신에서 나온 x,y,z,vx,vy)을
// 얻어야 한다. 서로 다른 갱신의 필드가 섞인 값은 허용되지 않는다.
// - 쓰기는 1개 스레드, 읽기는 다수.
//
// 아래 낙관적 스냅샷 구현을 검토하라.
using System.Threading;
public sealed class TransformSnapshot
{
private int _version;
private float _x, _y, _z, _vx, _vy;
// 물리 스레드(단일 writer): 새 트랜스폼을 게시
public void Write(float x, float y, float z, float vx, float vy)
{
_x = x; _y = y; _z = z; // (A) 필드 갱신
_vx = vx; _vy = vy;
_version++; // (B) 갱신 완료 표시
}
// 다수 reader: 일관된 스냅샷을 읽는다
public (float x, float y, float z, float vx, float vy) Read()
{
int v = _version; // (C) 버전 스냅샷
float x = _x, y = _y, z = _z, vx = _vx, vy = _vy;
// v 를 읽었으니 이 스냅샷은 버전 v 의 것이라고 간주한다.
return (x, y, z, vx, vy);
}
} 결함 코드 · C++
// 시나리오: 게임 서버 — 자주 읽히고 가끔 쓰이는 플레이어 트랜스폼 스냅샷 (C++)
//
// 물리 스레드 1개가 트랜스폼(위치/속도)을 갱신하고, 다수 스레드가 매우 빈번히
// 읽는다. 읽기가 압도적이라 뮤텍스 대신 "버전 카운터 기반 낙관적 읽기"(seqlock)로
// 설계했다.
//
// 요구사항:
// - 읽는 쪽은 항상 "한 갱신에서 나온 일관된 스냅샷"을 얻어야 한다(서로 다른 갱신의 필드가 섞이면 안 된다).
// - 쓰기 1 스레드, 읽기 다수.
//
// 아래 낙관적 스냅샷 구현을 검토하라.
#include <cstdint>
struct Vec3 { float x, y, z; };
class TransformSnapshot {
public:
// 물리 스레드(단일 writer)
void Write(const Vec3& p, float vx, float vy) {
seq_++; // (A) 갱신 시작
pos_ = p; // (B) 필드 갱신
vx_ = vx; vy_ = vy;
}
// 다수 reader
void Read(Vec3& outPos, float& outVx, float& outVy) {
uint32_t s;
do {
s = seq_; // (C) 시작 시퀀스
outPos = pos_; // 필드 읽기
outVx = vx_; outVy = vy_;
} while (s != seq_); // (D) 그 사이 갱신됐으면 재시도
}
private:
uint32_t seq_ = 0;
Vec3 pos_{};
float vx_ = 0, vy_ = 0;
}; 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.
내 리뷰 · C++
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.