본문 바로가기
cs공부/운영체제

운영체제 - TLS(Thread Local Storage)

by 코딩하는 돼징 2023. 6. 6.
반응형

게임 개발 요소

게임 로직 : 플레이어의 입력처리, 캐릭터의 움직임, 퀘스트 로직과 같은 게임의 동작 및 규칙

로직 : 게임실행하는 과정에서 발생하는 정보,경고,오류 등을 기록

데이터베이스 : 플레이어정보, 아이템 정보등 게임에서 사용되어지는 데이터의 저장과 관리

클라이언트 섹션 : UI, 그래픽, 사운드, 네트워크등 게임을 실제로 실행되는 플랫폼에서의 사용자와의 상호작용을 담당

 

락을 모든 부분에다가 배치하면 발생하는 모든 문제들을 간단하게 해결할 수 있겠지만 사실은 치명적인 문제가 있다.

한쪽에 몰리는 경우 처리하기가 매우 어려워진다. 예를 들어 모든 유저들이 분산되서 위치하면 좋은데 다른 지역에 몰려 있는 경우 클라이언트 세션에서 같은 게임로직에 packet을 쏘게 된다. Lock의 개념이 상호배타적인데 몰리게 되면 한번에 한번씩 처리할 수 있게 된다. 처리할 수 있는 공간이 한정적이므로 멀티쓰레드로 돌리는게 오히려 안좋을 수도 있다.

 

무조건 멀티쓰레드라고 Lock만 걸고 들어가는게 최선의 방법이 아니다.


해결 방법

TLS(일감 분배)

각 쓰레드에 대해 개별적으로 할당 되는 메모리 영역이다. 이는 쓰레드별로 데이터를 저장하고 접근할 수 있게 한다. 쓰레드는 자신의 TLS에 저장된 데이터에만 접근할 수 있으며 다른 쓰레드의 TLS에는 접근할 수 없다. 

TLS - Heap과 관계 : Heap에 TLS데이터가 저장된다.

TLS - Stack과 관계: Stack에 쓰레드에서 TLS에 접근하기 위한 포인터가 저장된다.


사용이유 

멀티 쓰레드 환경에서 전역 변수로 사용할 경우 모든 쓰레드가 공유하게 되어 동시에 접근하면 데이터의 일관성을 유지하기 어렵다. TLS는 각 쓰레드 별로 개별적으로 할당된 메모리이기 때문에 데이터 분리가 가능해진다. 또한 각 쓰레드별로 고유한 상태를 가질 수 있는데 이를 독립적으로 유지할 수 있게 된다.


병렬처리 알아보러가기

 

운영체제 - 병렬처리(Parallel Loops, Parallel Invoke,Parallel Partitioning)

순차처리 한 번에 하나씩 처리하는 방식, 이는 서로 독립적으로 실행한다. 병렬처리 여러 작업을 동시에 처리하는 방식, 이는 작업을 분할하면서 동시에 실행할 수 있다. 그러므로 실행 순서가

code-piggy.tistory.com


코드 예시

01 전역변수 사용한 경우

ThreadName은 공유 변수이므로 모든 쓰레드에서 동일한 값을 가진다. 

static string ThreadName;
static void WhoAmI()
{
    ThreadName= $"My Name is {Thread.CurrentThread.ManagedThreadId}";
    Thread.Sleep(1000);
    Console.WriteLine(ThreadName);
}

static void Main(string[] args)
{
    Parallel.Invoke(WhoAmI, WhoAmI, WhoAmI, WhoAmI, WhoAmI);
}

Parallel.Invoke메서드에서 WhoAmI 메서드가 병렬로 실행될동안 ThreadName변수에 동일한 값을 할당하고 출력하게 된다. 그 결과 아래와 같이 나온다.


02 TLS 사용한 경우

ThreadLocal을 통해서 쓰레드마다 고유한 값을 유지할 수 있도록한다.

ThreadLocal이란

쓰레드 별로 고유한 값의 저장과 접근을 가능하게 해주는 클래스이다. 

static ThreadLocal<string> ThreadName = new ThreadLocal<string>();

static void WhoAmI()
{
    ThreadName.Value = $"My Name is {Thread.CurrentThread.ManagedThreadId}";
    Thread.Sleep(1000);
    Console.WriteLine(ThreadName.Value);
}

static void Main(string[] args)
{
    Parallel.Invoke(WhoAmI, WhoAmI, WhoAmI, WhoAmI, WhoAmI);
}

ThreadName.Value를 통해서 쓰레드의 개별적인 값을 가져오므로 각 다른 값들이 출력된다.


03 IsValueCreated 사용해서 중복 체크

public bool IsValueCreated { get; }

속성 값

현재 쓰레드에서 Value가 초기화되었으면 true이고 그렇지 않으면 false의 결과가 나온다.

// 초기값 팩토리함수(현재 쓰레드에 대한 초기값을 생성하고 반환)를 지정하여 초기값을 생성
static ThreadLocal<string> ThreadName = new ThreadLocal<string>(() => { return $"My Name is {Thread.CurrentThread.ManagedThreadId}"; });

static void WhoAmI()
{
    bool repeat = ThreadName.IsValueCreated; // 현재 쓰레드의 초기값이 이미 생성되었는지 여부 확인
    if (repeat)
        Console.WriteLine(ThreadName.Value + "(repeat)"); // 초기값을 가져와 출력
    else
        Console.WriteLine(ThreadName.Value);
}

static void Main(string[] args)
{
    ThreadPool.SetMinThreads(1, 1);
    ThreadPool.SetMaxThreads(3, 3);
    Parallel.Invoke(WhoAmI, WhoAmI, WhoAmI, WhoAmI, WhoAmI, WhoAmI);
}

중복된 값이 나오는 이유

Parallel.Invoke를 호출할때 병렬처리로  쓰레드가 WhoAmI를 실행한다. ThreadName.Value에는 현재 쓰레드의 이름이 저장되고 병렬처리로 진행되기 때문에 동일한 ManagedThreadId를 가지게 되는 것이다.

 

 

 

 

 

 

 

참고 :  본 내용은 MMORPG  PART4 강의를 수강하여 작성하였습니다.

https://www.inflearn.com/course/%EC%9C%A0%EB%8B%88%ED%8B%B0-mmorpg-%EA%B0%9C%EB%B0%9C-part4

반응형

댓글