28. 공유 설정의 제자리 수정과 안전 공개(찢긴 스냅샷) (C#)
난이도 최상 해설 보기 →
결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커
(A)(B) 는 주목 위치 힌트다.
결함 코드 · C#
// 시나리오: 서버가 런타임에 월드/밸런스 설정을 갱신한다(핫 리로드).
// - 갱신 스레드가 현재 설정 객체의 필드를 새 값으로 바꾼다.
// - 다수 워커 스레드가 매 틱 _current 의 필드를 읽어 처리한다.
//
// 요구사항:
// - 워커는 항상 "일관된 하나의 설정 스냅샷"을 본다(필드 간 불일치/찢긴 상태 금지).
// - 갱신(쓰기)과 읽기가 동시에 일어나도 안전해야 한다.
//
// 참고: C# 에서 "참조 형식 변수에 대한 대입"은 원자적이라 참조가 찢기지는 않는다.
// 그렇다면 이 코드는 안전한가? 아래 (A)(B) 가 동시에 일어날 때를 보라.
//
// 과제: 이 갱신 방식이 동시 환경에서 왜 깨지는지 (A)(B) 지점을 중심으로 진단하고
// 고쳐라. (C++ 트윈과의 언어 차이도 설명할 것.)
using System.Collections.Generic;
public sealed class Config
{
public int Version;
public List<int> DropTable = new();
public string MapName = "";
}
public sealed class World
{
private Config _current = new Config();
public int RollDrop(int seed)
{
Config c = _current; // (A) 스냅샷 의도
int n = c.DropTable.Count; // (A) 필드 읽기 1
return n == 0 ? 0 : c.DropTable[seed % n]; // (A) 필드 읽기 2 (같은 c)
}
// 갱신 스레드: 현재 설정 객체를 "제자리(in place)"에서 수정
public void Reload(int version, List<int> drops, string map)
{
_current.Version = version; // (B) 제자리 수정
_current.DropTable = drops; // (B) 제자리 수정
_current.MapName = map; // (B) 제자리 수정
}
} 결함 코드 · C++
// 시나리오: 서버가 런타임에 월드/밸런스 설정 스냅샷을 통째로 교체한다(핫 리로드).
// - 갱신 스레드가 주기적으로 새 Config 를 만들어 current_ 에 대입한다.
// - 다수 워커 스레드가 매 틱 current_ 를 읽어(복사) 그 스냅샷으로 처리한다.
// Config 는 한번 만들면 불변(immutable)이며, 읽는 쪽은 자신이 잡은 스냅샷을
// 그대로 쓴다(스냅샷 자체는 안 바꾼다).
//
// 요구사항:
// - 워커는 항상 "완전히 만들어진 하나의 Config" 를 본다.
// - 갱신(쓰기)과 읽기가 동시에 일어나도 안전해야 한다.
//
// 참고: shared_ptr 의 "참조 카운트(제어블록)"는 스레드 안전하다. 그렇다면 이
// 코드도 안전한가? 아래 (A)(B) 가 같은 current_ 에 동시에 일어날 때를 보라.
//
// 과제: shared_ptr 를 이렇게 쓰는 것이 동시 환경에서 왜 안전하지 않은지
// (A)(B) 지점을 중심으로 진단하고 고쳐라.
#include <memory>
#include <string>
#include <vector>
struct Config {
int version = 0;
std::vector<int> dropTable;
std::string mapName;
};
class World {
public:
// 워커: 현재 설정 스냅샷을 얻는다.
std::shared_ptr<Config> current() const {
return current_; // (A) 같은 shared_ptr 객체를 복사(읽기)
}
// 갱신 스레드: 새 설정으로 교체.
void reload(std::shared_ptr<Config> next) {
current_ = std::move(next); // (B) 같은 shared_ptr 객체에 재대입(쓰기)
}
private:
std::shared_ptr<Config> current_ = std::make_shared<Config>();
}; 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.
내 리뷰 · C++
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.