33. 자동 파티 매칭의 정원 초과 (동시 배치 경합)

난이도 하 해설 보기 →

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

결함 코드 · C#
// 시나리오: MMORPG 자동 파티 매칭(오토 매칭).
// 매칭 워커 여러 개가 대기열의 플레이어를 "빈자리가 있는 파티"에 채워 넣는다.
// 같은 파티 객체에 여러 워커가 동시에 플레이어를 배치하려고 시도할 수 있다.
//
// 요구사항:
//  - 한 파티의 인원은 정원(MaxMembers = 4)을 절대 넘으면 안 된다.
//  - 정원이 찬 파티에는 더 이상 배치하지 않는다.
//  - 자리를 얻지 못한 플레이어는 호출 측이 대기열로 되돌린다(여기서는 false 반환으로 표현).
//
// 과제: (A)(B) 지점을 중심으로 동시성 관점에서 TryAdd 를 검토하라.
//       정원이 지켜지지 않을 수 있는 실행 순서가 있는가? 있다면 재현 조건과
//       수정안(정확성을 보장하는 코드)을 제시하라.

using System;
using System.Collections.Generic;
using System.Threading.Tasks;

public sealed class Party
{
    public const int MaxMembers = 4;
    public int PartyId { get; }
    private readonly List<int> _members = new();   // 플레이어 ID 목록

    public Party(int id) => PartyId = id;

    public int Count => _members.Count;

    // 매칭 워커가 호출한다. 자리가 있으면 추가 후 true, 가득 찼으면 false.
    public bool TryAdd(int playerId)
    {
        if (_members.Count >= MaxMembers)   // (A)
            return false;

        // 슬롯 배정·입장 알림 준비 등 약간의 처리 시간이 든다.
        AssignSlot(playerId);

        _members.Add(playerId);             // (B)
        return true;
    }

    private void AssignSlot(int playerId)
    {
        // 슬롯 인덱스 계산/입장 패킷 구성 등(상세 생략).
    }
}

public static class AutoMatcher
{
    // 한 파티의 빈자리를 대기열 플레이어들로 동시에 채운다.
    public static async Task FillAsync(Party party, IReadOnlyList<int> waiting)
    {
        var tasks = new List<Task>(waiting.Count);
        foreach (var pid in waiting)
        {
            int p = pid;
            tasks.Add(Task.Run(() =>
            {
                if (!party.TryAdd(p))
                {
                    // 자리가 없으면 대기열로 되돌린다(상세 생략).
                }
            }));
        }
        await Task.WhenAll(tasks);
    }
}
내 리뷰 · C#
내 답안 · 자동 저장

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