31. 환율 갱신과 환전의 경합 (시세 정합성 / 잔액 lost update)
난이도 중 해설 보기 →
결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커
(A)(B) 는 주목 위치 힌트다.
결함 코드 · C#
// ============================================================================
// [코드리뷰 문제] C# - 환율이 갱신되는 순간에 환전 요청이 들어오는 상황
// ----------------------------------------------------------------------------
// 시나리오 (거래/경제 - 환전소):
// - 게임 내 환전소는 골드를 이벤트 코인으로 바꿔 준다.
// - 시세(환율)는 시장 변동에 따라 백그라운드 갱신 스레드가 주기적으로 바꾼다.
// - 플레이어는 골드 금액을 입력해 환전을 요청하고, 서버는 "현재 환율"로 코인을 계산해
// 골드를 차감하고 코인을 지급한다.
//
// 상황:
// - 갱신 스레드가 currentRate_ 를 새 값으로 바꾸는 바로 그 순간에, 여러 플레이어 요청
// 스레드가 Exchange 를 동시에 호출한다.
// - 같은 플레이어가 빠르게 두 번(또는 클라 재시도로 동시에) 환전을 보낼 수도 있다.
//
// 과제:
// 이 코드의 잠재적 문제를 모두 찾아라. 환율 갱신과 환전이 겹칠 때 지급되는 코인 양과
// 플레이어 골드 잔액이 어떤 상태가 될 수 있는지, 그리고 같은 플레이어의 동시 환전 두 건이
// 어떻게 처리되는지 설명하라. 수정안과 더 나은 설계를 제시하라.
// (먼저 직접 리뷰를 적은 뒤 answer.md 와 대조할 것)
// ============================================================================
using System.Threading;
public interface IWallet
{
long GetGold(long playerId);
void SetGold(long playerId, long gold);
void AddCoin(long playerId, long coin);
}
public class ExchangeMarket
{
private double currentRate_ = 1.0; // 골드 1 당 코인 (예: 0.85)
// 백그라운드 갱신 스레드가 호출
public void UpdateRate(double newRate)
{
currentRate_ = newRate; // (A) 시세 반영
}
public double CurrentRate => currentRate_; // (B) 시세 조회
}
public class ExchangeService
{
private readonly IWallet _wallet;
private readonly ExchangeMarket _market;
public ExchangeService(IWallet wallet, ExchangeMarket market)
{ _wallet = wallet; _market = market; }
// 여러 플레이어 요청 스레드에서 동시 호출될 수 있다.
public bool Exchange(long playerId, long goldAmount)
{
if (goldAmount <= 0) return false;
double rate = _market.CurrentRate; // (C) 시세 스냅샷
long coins = (long)(goldAmount * rate); // (D) 환산
long bal = _wallet.GetGold(playerId); // (E) 잔액 조회
if (bal < goldAmount) return false; // (F) 잔액 확인
_wallet.SetGold(playerId, bal - goldAmount); // (G) 골드 차감
_wallet.AddCoin(playerId, coins); // (H) 코인 지급
return true;
}
} 결함 코드 · C++
// ============================================================================
// [코드리뷰 문제] C++ - 환율이 갱신되는 순간에 환전 요청이 들어오는 상황
// ----------------------------------------------------------------------------
// 시나리오 (거래/경제 - 환전소):
// - 게임 내 환전소는 골드를 이벤트 코인으로 바꿔 준다.
// - 시세(환율)는 시장 변동에 따라 백그라운드 갱신 스레드가 주기적으로 바꾼다.
// - 플레이어는 골드 금액을 입력해 환전을 요청하고, 서버는 "현재 환율"로 코인을 계산해
// 골드를 차감하고 코인을 지급한다.
//
// 상황:
// - 갱신 스레드가 rate_ 를 새 값으로 바꾸는 바로 그 순간에, 여러 요청 스레드가
// exchange 를 동시에 호출한다.
// - 같은 플레이어가 거의 동시에 두 번 환전을 보낼 수도 있다.
//
// 과제:
// 이 코드의 잠재적 문제를 모두 찾아라. 갱신 스레드의 rate_ 쓰기와 요청 스레드의 rate_
// 읽기가 어떤 관계인지, 지급 코인 양과 골드 잔액이 어떤 상태가 될 수 있는지 설명하라.
// 동시 환전 두 건의 처리도 함께 분석하고, 수정안과 더 나은 설계를 제시하라.
// (먼저 직접 리뷰를 적은 뒤 answer.md 와 대조할 것)
// ============================================================================
#include <cstdint>
#include <unordered_map>
struct Wallet {
long getGold(long playerId);
void setGold(long playerId, long gold);
void addCoin(long playerId, long coin);
};
class ExchangeMarket {
public:
// 백그라운드 갱신 스레드가 호출
void updateRate(double newRate) {
rate_ = newRate; // (A) 시세 반영
}
double currentRate() const {
return rate_; // (B) 시세 조회
}
private:
double rate_ = 1.0; // 골드 1 당 코인
};
class ExchangeService {
public:
ExchangeService(Wallet& w, ExchangeMarket& m) : wallet_(w), market_(m) {}
// 여러 요청 스레드에서 동시 호출될 수 있다.
bool exchange(long playerId, long goldAmount) {
if (goldAmount <= 0) return false;
double rate = market_.currentRate(); // (C) 시세 스냅샷
long coins = static_cast<long>(goldAmount * rate); // (D) 환산
long bal = wallet_.getGold(playerId); // (E) 잔액 조회
if (bal < goldAmount) return false; // (F) 잔액 확인
wallet_.setGold(playerId, bal - goldAmount); // (G) 골드 차감
wallet_.addCoin(playerId, coins); // (H) 코인 지급
return true;
}
private:
Wallet& wallet_;
ExchangeMarket& market_;
}; 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.
내 리뷰 · C++
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.