33. 매칭 레이팅 갱신 vs 매칭 진행: 찢긴 스냅샷과 갱신 유실 (C#)

난이도 하 해설 보기 →

결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커 (A)(B) 는 주목 위치 힌트다.

결함 코드 · C#
// [시나리오] 매칭 레이팅 갱신과 매칭 진행의 경합 (서버 내부)
// 플레이어는 매칭 레이팅(Mmr)과 그로부터 파생된 티어(Tier)를 가진다.
// - 매칭 워커: 대기열의 플레이어를 Mmr 로 브래킷(실력 구간)에 배치한다.
// - 매치 결과 워커: 매치가 끝나면 승패에 따라 Mmr 을 조정하고 Tier 를 다시 계산한다.
// 두 종류의 워커는 서로 다른 스레드에서 같은 Player 를 동시에 건드릴 수 있다
// (한 플레이어가 방금 끝난 매치의 정산과, 다음 매치 대기열 배치가 겹치는 순간 등).
//
// [요구사항]
//   - 브래킷 배치는 그 시점의 Mmr 과 일관된 값이어야 한다(Mmr 과 Tier 가 어긋나면 안 됨).
//   - 여러 매치 결과가 거의 동시에 정산돼도 Mmr 조정이 유실되면 안 된다.
//
// [과제] Matchmaker 의 PlaceInBracket 과 ApplyResult 를 검토하고, 두 워커가
//        같은 플레이어를 동시에 처리할 때 무엇이 어긋나는지 원인과 수정안을
//        제시하라. 표시된 (A)(B) 지점에 주목하라.

using System;

class Player
{
    public int PlayerId;
    public int Mmr;      // 매칭 레이팅
    public int Tier;     // Mmr 에서 파생
}

class Matchmaker
{
    private const int BracketWidth = 100;
    private static int ComputeTier(int mmr) => mmr / 500;

    // 매칭 워커 스레드에서 호출된다.
    public int PlaceInBracket(Player p)
    {
        // (A)
        int mmr = p.Mmr;
        return mmr / BracketWidth;
    }

    // 매치 결과 워커 스레드에서 호출된다(여러 매치가 동시에 끝날 수 있음).
    public void ApplyResult(Player p, int delta)
    {
        // (B)
        p.Mmr = p.Mmr + delta;
        p.Tier = ComputeTier(p.Mmr);
    }
}
내 리뷰 · C#
내 답안 · 자동 저장

작성 후 위 해설 보기에서 모범 해설과 대조하세요.