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++
// 시나리오: MMORPG 자동 파티 매칭(오토 매칭).
// 매칭 워커 스레드 여러 개가 대기열의 플레이어를 "빈자리가 있는 파티"에 채워 넣는다.
// 같은 Party 객체에 여러 스레드가 동시에 플레이어를 배치하려고 시도할 수 있다.
//
// 요구사항:
// - 한 파티의 인원은 정원(kMaxMembers = 4)을 절대 넘으면 안 된다.
// - 정원이 찬 파티에는 더 이상 배치하지 않는다.
// - 자리를 얻지 못한 플레이어는 호출 측이 대기열로 되돌린다(false 반환으로 표현).
//
// 과제: (A)(B) 지점을 중심으로 동시성 관점에서 try_add 를 검토하라.
// 정원이 지켜지지 않을 수 있는 실행 순서가 있는가? 있다면 재현 조건과
// 수정안(정확성을 보장하는 코드)을 제시하라.
#include <vector>
#include <thread>
#include <cstdio>
class Party {
public:
static constexpr int kMaxMembers = 4;
explicit Party(int id) : party_id_(id) {}
int count() const { return static_cast<int>(members_.size()); }
// 매칭 워커가 호출한다. 자리가 있으면 추가 후 true, 가득 찼으면 false.
bool try_add(int player_id) {
if (static_cast<int>(members_.size()) >= kMaxMembers) // (A)
return false;
assign_slot(player_id); // 슬롯 배정/입장 알림 준비 등 약간의 처리
members_.push_back(player_id); // (B)
return true;
}
private:
void assign_slot(int /*player_id*/) {
// 슬롯 인덱스 계산/입장 패킷 구성 등(상세 생략).
}
int party_id_;
std::vector<int> members_;
};
int main() {
Party party(1);
int waiting[] = {101, 102, 103, 104, 105, 106, 107, 108};
std::vector<std::thread> ts;
for (int pid : waiting) {
ts.emplace_back([&party, pid] {
party.try_add(pid); // 자리가 없으면 대기열 복귀(생략)
});
}
for (auto& t : ts) t.join();
std::printf("party size = %d (max %d)\n", party.count(), Party::kMaxMembers);
return 0;
} 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.
내 리뷰 · C++
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.