51. 재사용 배리어의 세대(phase) 관리 부재로 인한 프레임 섞임·데드락 · C#

난이도 최상 해설 보기 →

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

결함 코드 · C#
// 시나리오: 틱 루프 워커 동기화용 재사용 배리어 (C#)
//
// 게임 서버의 틱 루프는 N개의 워커 스레드가 매 프레임 각자 일부 엔티티를 갱신한 뒤,
// 모두 끝날 때까지 만나서 기다렸다가(rendezvous) 다음 단계(충돌 판정 등)로 넘어간다.
// 이 만남을 위해 매 프레임 재사용되는 배리어를 직접 구현했다.
//
// 각 워커는 프레임마다 SignalAndWait() 를 호출한다. 모든 워커(_parties 명)가
// 도착하면 함께 풀려나 다음 프레임을 시작한다. 배리어는 여러 프레임에 걸쳐 재사용된다.
//
// 요구사항:
//  - 매 프레임, 모든 워커가 도착해야 아무도 다음 단계로 못 넘어간다(정확한 rendezvous).
//  - 배리어는 프레임마다 다시 쓸 수 있어야 한다.
//
// 워커 수는 4, 프레임은 수천 번 반복된다고 하자.
//
// 과제:
//  1) (A)(B) 를 중심으로, 배리어를 프레임마다 재사용할 때 무엇이 깨지는지 설명하라.
//  2) 특정 인터리빙에서 정확성이 어떻게 깨지는지(일부 워커 멈춤 또는 정족수 미달 통과) 제시하라.
//  3) 수정안을 제시하라.

using System.Threading;

public class TickBarrier
{
    private readonly int _parties;
    private int _waiting = 0;
    private readonly object _gate = new object();

    public TickBarrier(int parties) { _parties = parties; }

    public void SignalAndWait()
    {
        lock (_gate)
        {
            _waiting++;

            if (_waiting == _parties)
            {
                // 마지막 도착자: 다음 프레임을 위해 리셋하고 전원 깨움
                _waiting = 0;                 // (B)
                Monitor.PulseAll(_gate);
            }
            else
            {
                // 아직 다 안 왔으면 리셋될 때까지 대기
                while (_waiting != 0)         // (A)
                    Monitor.Wait(_gate);
            }
        }
    }
}
내 리뷰 · C#
내 답안 · 자동 저장

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