42. 원자 변수의 복합 연산 비원자성: peak 갱신 유실과 찢긴 평균 쌍 (C#)
난이도 상 해설 보기 →
결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커
(A)(B) 는 주목 위치 힌트다.
결함 코드 · C#
// 시나리오: 실시간 전투 통계 집계기. 여러 게임 스레드가 동시에
// (1) 현재 동시 전투 참가자 수를 보고하며 "관측된 최고치(peak)"를 갱신하고,
// (2) 히트가 발생할 때마다 총 데미지와 히트 횟수를 누적한다.
// 대시보드는 언제든 평균 데미지(총 데미지 / 히트 횟수)를 조회한다.
//
// 요구사항:
// 1) peak 은 관측된 실제 최댓값이어야 한다(동시 보고에도 유실 없이).
// 2) 평균 = 총데미지 / 히트횟수 는 서로 짝이 맞는(찢기지 않은) 값으로 계산돼야 한다.
// 3) 원자 변수를 쓰더라도 최종 결과가 정확해야 한다.
//
// 아래 집계기를 여러 스레드가 recordConcurrent/recordHit 를 동시에 호출하고
// 대시보드가 AverageDamage 를 동시에 읽는다는 관점에서 검토하고,
// (A)(B) 지점을 중심으로 문제를 진단·수정하라.
using System.Threading;
public class BattleStats
{
private long _peakConcurrent;
private long _totalDamage;
private long _hitCount;
public void RecordConcurrent(long current)
{
// (A) 현재값이 지금까지의 최고치보다 크면 최고치를 갱신한다.
if (current > Interlocked.Read(ref _peakConcurrent))
Interlocked.Exchange(ref _peakConcurrent, current);
}
public void RecordHit(long damage)
{
// (B) 총 데미지와 히트 횟수를 각각 누적한다.
Interlocked.Add(ref _totalDamage, damage);
Interlocked.Increment(ref _hitCount);
}
public long Peak => Interlocked.Read(ref _peakConcurrent);
public double AverageDamage()
{
long total = Interlocked.Read(ref _totalDamage);
long count = Interlocked.Read(ref _hitCount);
return count == 0 ? 0.0 : (double)total / count;
}
} 결함 코드 · C++
// 시나리오: 실시간 전투 통계 집계기 (C++ 서버). 여러 게임 스레드가 동시에
// (1) 현재 동시 전투 참가자 수를 보고하며 "관측된 최고치(peak)"를 갱신하고,
// (2) 히트가 발생할 때마다 총 데미지와 히트 횟수를 누적한다.
// 대시보드는 언제든 평균 데미지(총 데미지 / 히트 횟수)를 조회한다.
//
// 요구사항:
// 1) peak 은 관측된 실제 최댓값이어야 한다(동시 보고에도 유실 없이).
// 2) 평균 = 총데미지 / 히트횟수 는 서로 짝이 맞는(찢기지 않은) 값으로 계산돼야 한다.
// 3) 원자 변수를 쓰더라도 최종 결과가 정확해야 한다.
//
// 아래 집계기를 여러 스레드가 recordConcurrent/recordHit 를 동시에 호출하고
// 대시보드가 averageDamage 를 동시에 읽는다는 관점에서 검토하고,
// (A)(B) 지점을 중심으로 문제를 진단·수정하라.
#include <atomic>
#include <cstdint>
class BattleStats {
public:
void recordConcurrent(int64_t current) {
// (A) 현재값이 지금까지의 최고치보다 크면 최고치를 갱신한다.
if (current > peakConcurrent_.load(std::memory_order_relaxed))
peakConcurrent_.store(current, std::memory_order_relaxed);
}
void recordHit(int64_t damage) {
// (B) 총 데미지와 히트 횟수를 각각 누적한다.
totalDamage_.fetch_add(damage, std::memory_order_relaxed);
hitCount_.fetch_add(1, std::memory_order_relaxed);
}
int64_t peak() const { return peakConcurrent_.load(std::memory_order_relaxed); }
double averageDamage() const {
int64_t total = totalDamage_.load(std::memory_order_relaxed);
int64_t count = hitCount_.load(std::memory_order_relaxed);
return count == 0 ? 0.0 : static_cast<double>(total) / count;
}
private:
std::atomic<int64_t> peakConcurrent_{0};
std::atomic<int64_t> totalDamage_{0};
std::atomic<int64_t> hitCount_{0};
}; 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.
내 리뷰 · C++
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.