47. 2-스레드 상호 배제의 StoreLoad 재배열: acquire/release로는 부족 (C#)
난이도 최상 해설 보기 →
결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커
(A)(B) 는 주목 위치 힌트다.
결함 코드 · C#
// 시나리오: 게임 서버의 두 전용 스레드(틱 스레드 T0, 스냅샷 스레드 T1)가
// 공유 버퍼에 "짧고 드물게" 상호 배타적으로 접근해야 한다.
// 운영자는 이 핫패스에서 OS 뮤텍스 비용을 피하려고, 두 스레드 전용의
// 경량 상호 배제(2-스레드 Peterson 방식)를 손수 구현했다.
// - _want[i] : 스레드 i 가 임계 구역 진입을 원함
// - _turn : 경합 시 양보 순번
//
// 요구사항:
// 1) 어느 순간에도 두 스레드가 동시에 임계 구역(공유 버퍼 접근)에 있어선 안 된다(상호 배제).
// 2) 이 코드는 정확히 두 스레드(id 0, 1)만 호출한다.
//
// 참고: Peterson 알고리즘 자체는 "순차 일관(sequential consistency)" 가정에서는
// 상호 배제가 증명된다. 아래 구현은 volatile 필드로 가시성을 준다고 보았다.
//
// 과제: Enter/Exit 를 검토하여, 두 스레드가 동시에 임계 구역에 들어갈 수 있는
// 지점을 (A)(B) 마커로 진단하고, 정확한 수정안을 제시하라.
using System.Threading;
public sealed class TwoThreadLock
{
private readonly bool[] _want = new bool[2];
private int _turn;
public void Enter(int me)
{
int other = 1 - me;
Volatile.Write(ref _want[me], true); // (A) 진입 의사 게시
Volatile.Write(ref _turn, other);
// (B) 상대 의사 + 순번 확인. 상대가 원하고 순번이 상대면 대기(양보).
while (Volatile.Read(ref _want[other]) && Volatile.Read(ref _turn) == other)
{
Thread.SpinWait(1);
}
// 여기 도달 = 임계 구역 진입
}
public void Exit(int me)
{
Volatile.Write(ref _want[me], false);
}
} 결함 코드 · C++
// 시나리오: 게임 서버의 두 전용 스레드(틱 T0, 스냅샷 T1)가 공유 버퍼에
// 짧고 드물게 상호 배타적으로 접근해야 한다. 핫패스에서 뮤텍스 비용을 피하려
// 2-스레드 Peterson 방식 경량 상호 배제를 손수 구현했다.
// - want_[i] : 스레드 i 의 진입 의사
// - turn_ : 경합 시 양보 순번
//
// 요구사항:
// 1) 어느 순간에도 두 스레드가 동시에 임계 구역에 있어선 안 된다(상호 배제).
// 2) 정확히 두 스레드(id 0, 1)만 호출한다.
//
// 참고: Peterson 알고리즘은 "순차 일관" 가정에서 상호 배제가 증명된다.
// 아래 구현은 std::atomic 의 release/acquire 순서를 사용한다.
//
// 과제: enter/exit 를 검토하여, 두 스레드가 동시에 임계 구역에 들어갈 수 있는
// 지점을 (A)(B) 마커로 진단하고, 정확한 수정안을 제시하라.
//
// 컴파일: g++ -std=c++17 -pthread -fsyntax-only problem.cpp
#include <atomic>
#include <thread>
class TwoThreadLock
{
public:
void enter(int me)
{
int other = 1 - me;
want_[me].store(true, std::memory_order_release); // (A) 진입 의사 게시
turn_.store(other, std::memory_order_release);
// (B) 상대 의사 + 순번 확인. 상대가 원하고 순번이 상대면 대기.
while (want_[other].load(std::memory_order_acquire) &&
turn_.load(std::memory_order_acquire) == other)
{
std::this_thread::yield();
}
// 여기 도달 = 임계 구역 진입
}
void exit(int me)
{
want_[me].store(false, std::memory_order_release);
}
private:
std::atomic<bool> want_[2]{{false}, {false}};
std::atomic<int> turn_{0};
}; 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.
내 리뷰 · C++
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.