4. 경매장 동시 입찰
난이도 상 해설 보기 →
결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커
(A)(B) 는 주목 위치 힌트다.
결함 코드 · C#
// ============================================================================
// [코드리뷰 문제] C# - 경매장 동시 입찰
// ----------------------------------------------------------------------------
// 시나리오:
// - 플레이어가 아이템을 경매에 등록한다(시작가, 즉시구매가, 마감시각).
// - 다른 플레이어들이 동시에 입찰한다. 현재 최고가보다 높아야 입찰 성공.
// - 즉시구매가에 도달하면 즉시 낙찰. 마감되면 최고 입찰자가 낙찰.
// - 입찰에 성공하면 입찰액만큼 골드가 묶이고(에스크로), 더 높은 입찰자가
// 나타나면 이전 입찰자의 골드는 환불된다.
//
// 요구사항:
// - 수많은 IO 스레드가 같은 경매에 동시에 입찰 패킷을 처리한다.
// - 최고가/최고입찰자는 항상 정합적이어야 한다.
// - 골드는 묶인 만큼 정확히 환불/차감되어야 한다(증발/복사 금지).
//
// 과제:
// 이 코드의 잠재적 문제를 모두 찾고, 왜 문제인지 설명하고,
// 수정안과 더 나은 설계를 제시하라.
// (먼저 직접 리뷰를 적은 뒤 answer.md 와 대조할 것)
// ============================================================================
using System;
using System.Collections.Generic;
using System.Threading;
public class Player
{
public long Id;
public long Gold; // 다른 스레드도 접근
}
public class Auction
{
private readonly long _itemId;
private readonly long _buyout;
private readonly long _closeTick;
private long _highestBid; // Interlocked 로 갱신
private long _highestBidder = -1; // Interlocked 로 갱신
private int _closed; // 0/1, Interlocked 로 갱신
public Auction(long itemId, long startPrice, long buyout, long closeTick)
{
_itemId = itemId;
_buyout = buyout;
_closeTick = closeTick;
_highestBid = startPrice;
}
// 입찰. 성공하면 true. players 에서 골드 묶기/환불 처리.
public bool Bid(Player bidder, long amount, long nowTick,
Dictionary<long, Player> players)
{
if (nowTick >= _closeTick) return false; // (A) 마감 체크
// (B) 현재 최고가 읽고 비교
long cur = Interlocked.Read(ref _highestBid);
if (amount <= cur) return false;
// (C) 새 최고가로 갱신
Interlocked.Exchange(ref _highestBid, amount);
long prevBidder = Interlocked.Exchange(ref _highestBidder, bidder.Id);
// (D) 새 입찰자 골드 묶기
Interlocked.Add(ref bidder.Gold, -amount);
// (E) 이전 입찰자 환불
if (prevBidder >= 0)
{
if (players.TryGetValue(prevBidder, out var prev))
Interlocked.Add(ref prev.Gold, cur); // (F)
}
// (G) 즉시구매 도달 시 낙찰
if (amount >= _buyout)
{
Interlocked.Exchange(ref _closed, 1);
}
return true;
}
public bool IsClosed(long nowTick)
{
return Interlocked.CompareExchange(ref _closed, 0, 0) == 1 || nowTick >= _closeTick;
}
} 결함 코드 · C++
// ============================================================================
// [코드리뷰 문제] C++ - 경매장 동시 입찰
// ----------------------------------------------------------------------------
// 시나리오:
// - 플레이어가 아이템을 경매에 등록한다(시작가, 즉시구매가, 마감시각).
// - 다른 플레이어들이 동시에 입찰한다. 현재 최고가보다 높아야 입찰 성공.
// - 즉시구매가에 도달하면 즉시 낙찰. 마감되면 최고 입찰자가 낙찰.
// - 입찰에 성공하면 입찰액만큼 골드가 묶이고(에스크로), 더 높은 입찰자가
// 나타나면 이전 입찰자의 골드는 환불된다.
//
// 요구사항:
// - 수많은 IO 스레드가 같은 경매에 동시에 입찰 패킷을 처리한다.
// - 최고가/최고입찰자는 항상 정합적이어야 한다.
// - 골드는 묶인 만큼 정확히 환불/차감되어야 한다(증발/복사 금지).
//
// 과제:
// 이 코드의 잠재적 문제를 모두 찾고, 왜 문제인지 설명하고,
// 수정안과 더 나은 설계를 제시하라.
// (먼저 직접 리뷰를 적은 뒤 answer.md 와 대조할 것)
// ============================================================================
#include <atomic>
#include <cstdint>
#include <unordered_map>
#include <mutex>
struct Player {
int64_t id;
std::atomic<int64_t> gold{0};
};
class Auction {
public:
Auction(int64_t itemId, int64_t startPrice, int64_t buyout, int64_t closeTick)
: itemId_(itemId), buyout_(buyout), closeTick_(closeTick),
highestBid_(startPrice), highestBidder_(-1) {}
// 입찰. 성공하면 true. players 에서 골드 묶기/환불 처리.
bool Bid(Player& bidder, int64_t amount, int64_t nowTick,
std::unordered_map<int64_t, Player*>& players) {
if (nowTick >= closeTick_) return false; // (A) 마감 체크
// (B) 현재 최고가 읽고 비교
int64_t cur = highestBid_.load();
if (amount <= cur) return false;
// (C) 새 최고가로 갱신
highestBid_.store(amount);
int64_t prevBidder = highestBidder_.exchange(bidder.id);
// (D) 새 입찰자 골드 묶기
bidder.gold.fetch_sub(amount);
// (E) 이전 입찰자 환불
if (prevBidder >= 0) {
auto it = players.find(prevBidder);
if (it != players.end())
it->second->gold.fetch_add(cur); // (F)
}
// (G) 즉시구매 도달 시 낙찰
if (amount >= buyout_) {
closed_.store(true);
}
return true;
}
bool IsClosed(int64_t nowTick) const {
return closed_.load() || nowTick >= closeTick_;
}
private:
int64_t itemId_;
int64_t buyout_;
int64_t closeTick_;
std::atomic<int64_t> highestBid_;
std::atomic<int64_t> highestBidder_;
std::atomic<bool> closed_{false};
}; 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.
내 리뷰 · C++
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.