윈도우의 프로세스와 스레드 (9)
윈도우의 프로세스와 스레드 (9)
TLS
스레드에서 전역 변수를 사용할경우 모든 스레드가 해당 변수를 건드리기 때문에, 다른 스레드가 전혀 동작하지않는다고 하더라도,
컨텍스트 전환의 결과로 특정 데이터를 엉망으로 만들어버릴 가능성이 높습니다.
이경우 스레드 로컬 저장소를 사용하면 문제가 깔끔히 해결됩니다.
스레드 로컬 저장 영역은 32비트 블록으로 지정되며, 각각의 스레드별로 사용 공간을 할당할수있습니다.
포인터로 값을 전달하기 좋아하는 C 계열 프로그래머들이 일반적인 멀티스레드 프로그램을 작성할때, 스레드와 해당 스레드가 호출하는 함수 사이에서
인수전달이 포인터로 이루어진다면 여러문제가 발생할수있습니다.
제일 큰 문제는 포인터가 전역변수로 지정되어야 한다는것입니다.
이말은 컨텍스트 스위칭이 일어나지 않더라도 각각의 멀티스레드 프로그램이 포인터로 지정된 변수를 일그러뜨릴 가능성이 매우 높다는것입니다.
가령 항상 초기값이 0으로 세팅되어 있어야 특정 스레드와 그 연관 함수가 만족하지만 스레드 수행 결과 그 포인터 변수는 초기값이 스레드의 결과값으로
치환된 이후 컨텍스트 전환이 일어난경우 다른 스레드는 엉뚱한 초기값을 받게 됩니다.
물론 이 부분은 뮤텍스나 세마포어등을 사용하여 초기값을 항상 원래대로 복귀하는것으로 해결할수있습니다.
그렇지만 뮤텍스나 세마포어의 비교적 수행속도가 느리고 경쟁상태나 교착상태의 가능성이 없이 단순히 변수의 변화를 막기위해 뮤텍스나 세마포어를
사용하는것은 비효율적입니다.
또한 스레드 동기화는 근본적으로 컨텍스트 전환은 계속 수행하고있는 상황이기에 CPU의 시간을 계속 소모하면서 대기하는방법을 사용하게 됩니다.
따라서 스레드와 그 스레드가 호출하는 함수 사이에서만 사용할수있는 포인터 변수를 도입하면 메모리 측면에서 모든 참조형 변수의 사본을 만드는 손해를
볼수있지만, 각각의 스레드를 병렬적으로 수행하면서 컨텍스트 전환에 의해 발생하는 문제들을 근원적으로 해결할수있습니다.
경쟁상태가 나타나지 않는 단순히 멀티스레드의 이점을 최대한 활용하기 위해 이런 방법론을 사용합니다.
물론 저장영역을 손해보고 메모리 연산을 빈번히 사용하기때문에 복잡해지는 경우 오류가능성이 더 커질수있습니다.
TLS는 각 스레드를 위한 전용 주소 공간을 제공하며 각 주소 공간의 인덱스로 해당 공간을 접근합니다.
이는 마치 지하철 물품보관소와 같이 각자가 물품보관소 열쇠를 갖고있고, 해당 열쇠를 가진 사람만이 물건을 넣고 꺼낼수있는 형태로 운영됩니다.
각 스레드는 호출되는 함수를 통해 자신이 삽입한 변수를 공유할수있습니다.
TLS 는 정적 또는 동적으로 할당할수있으며 마치 일반적인 cpp 프로그램의 메모리 할당과 비슷한 방법으로 동작하게됩니다.
정적 TLS 를 사용할떄 TLS 인덱스는 명시적으로 생성되거나 파괴되지 않습니다.
스레드를 생성될떄 TLS 저장영역이 생성되며 스레드가 파괴될때 같이 파괴됩니다.
주로 멀티스레드 프로그램을 수행하면서 정적 초기 변수가 변경될 가능성이 있지만
이러한 변수들을 제외하고 교착상태나 경쟁상태가 벌어지지 않을경우 TLS 는 유용합니다.
Reference
https://karfn84.tistory.com/entry/%ED%8E%8C%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-%EC%9C%88%EB%8F%84%EC%9A%B0%EC%9D%98-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EC%99%80-%EC%8A%A4%EB%A0%88%EB%93%9C