2007년 9월 20일 목요일

TLS - Thread Local Storage

쓰레드를 돌리다보면 해당 쓰레드에서만 유효한 변수를 만들고 싶을 때가 있다. 그럴 때 어떻게 해야할까? 쓰레드 개수만큼 벡터를 잡고, 해당 쓰레드마다 벡터 인덱스를 넘겨주는 것도 나름 방법이지만, 이러한 경우를 위해 TLS라는 것이 존재한다. 축약처럼 쓰레드 안에서만 따로 스토리지를 할당한 것인데, 일반 프로세스의 스텍과 비슷하나 그 크기는 프로세스 것보다 당연히 작고 제약이 심한 편이다. 뭐 암튼 그런게 있다고 치자.

* GCC 메뉴얼 보면 TLS가 아니라 TSD(Thread Storage Duration)이라고 표현하였는데 대충 같은 말이다.

이것을 위해 POSIX는 pthread_key_create, pthread_get/setspecific 등 함수를 마련해놨지만 눈만 팽글팽글 돌고, 소스만 지저분해져 보일 것 같다. 귀찮으면 C99, C++98 표준 __thread 키워드를 사용하자. MSDN에 보니 __thread가 없어 보인다. 이가 없으면 잇몸으로라도...

#define __thread __declspec( thread )

대충 이 정도면 될 듯하다. 자자 각설하고, __thread란 키워드는 TLS에 변수를 선언해주는 일을 한다. 위에 나열한 pthread_* 함수를 호출하는 것으로 컴파일러가 대신 해석해주는 것일 수도 있으나 이건 가정이므로 넘어가고. 사용에 주의해야할 점은 아래와 같다. (대충대충 표준하고 MSDN 보면서 정리한 것임)
  1. 글로벌 변수나 static 변수에만 사용 가능하다.
    - function 안에 local 변수가 static이 아니라면 뭐더게 TLS에 저장할까? 당연히 그럴 필요가 없기 때문에 저런 제약이 있는 것이다. 이는 class에서 멤버 변수에도 적용한 법칙이다. static으로 선언한 멤버 변수에만 저 키워드를 사용할 수 있다.
  2. SO(DLL)과 로컬 영역을 넘나들면 뻑난다.
    - ... 그러니까 쓰지마라. 왜?
  3. 타입에 대한 한정자로 사용이 불가능하다.
    - const처럼 쓰지 말라는 것이고, 무조건 타입 앞에 붙이란 말이다. (VC에만 해당)
  4. 객체 이름과 이름 없는 객체에만 쓸 수 있다.
  5. 앞에 붙을 수 있는 건 static과 extern 뿐이다.
  6. 클래스 자체를 TLS에 넣을 때는 생성자와 소멸자가 없어야 한다.
역시나 예가 없으면 아무 것도 할 수 없는 나를 위하여~*
#include <iostream>
using namespace std;

__thread int gTest1;      // 성공

__thread void func(void); // 실패

void
func(__thread int lTest2) // 실패
{
}

class A
{
    static __thread int mTest3;   // 성공
    __thread int mTest4;          // 실패
};

int
func(void)
{
    static __thread int lTest5; // 성공
    __thread int lTest6;   // 실패
}
__thread union
{
    int i;
    char p[sizeof(int)];
} gTest7;   // 성공
__thread struct
{
    int i;
    char p[sizeof(int)];
} gTest8;   // 성공

class MyClass
{
public:
    int i;
    MyClass() i(0) {}
};

__thread MyClass cTest9; // 실패

int __thread * gTest9; // gcc 성공

int
main(int,char**)
{
    return 0;
}

나머진 시간 나는대로 정리하도록 해야겠다.