Thread Local Storage (TLS)의 필요성

두 개의 쓰레드를 생성하는 다음 프로그램을 보자.

#include "stdafx.h"
#include
#include

// Thread관련
HANDLE TimerThreadHandle;
DWORD TimerThreadId;
DWORD WINAPI TimerThreadFunction( LPVOID lpParam )
{
int *i = (int*)lpParam;
int a = *i;
printf("addr of param i is %x and value: %d\n", i, a);
return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{
HANDLE handles[2];
//int a[2];
for(int i=0; i<2; i++)
{
//HeapAlloc(GetCurrentThread(), HEAP_ZERO_MEMORY, sizeof(int));
//a[i] = i;
int a = i;
handles[i] = CreateThread(
NULL,                   // default security attributes
0,                      // use default stack size
TimerThreadFunction,       // thread function name
&a, // argument to thread function
0,                      // use default creation flags
&TimerThreadId);   // returns the thread identifier
}
WaitForMultipleObjectsEx(2, handles, false, INFINITE, false);

return 0;
}


원래의 의도는 두 개의 서로 다른 주소(다른 값)을 가지는 쓰레드를 생성하는 것이었다.

쓰레드 생성시에 전달된 인자를 쓰레드 지역 변수에 저장까지 하니 확실히 인자를 독립적으로 두 개를 사용한다.

MSDN에도 쓰레드가 실행되는 코드의 지역변수는 독립적이라고 되어있다.

실행 결과는 다음과 같이 참담하다.


addr of param i is 12ff44 and value: 1
addr of param i is 12ff44 and value: 1


문제는 변수 저장소가 아니다. 쓰레드 함수가 실행되는 시점의 문제다.

main 루프에서 생성된 쓰레드는 쓰레드가 생성되자마자 수행되지 않았다. 만약 그랬다면 최소한 쓰레드 함수의 지역변수 a에 들어가는 값은 달라야 한다. 지역 포인터 변수 i야 그렇다 치더라도 (물론 실행해 보기 전까지 i에도 다른 값이 들어갈 거라고 의심없이 믿었다. 다른 값이 들어간걸 확인한 후에야 아~ 그렇구나 이렇게 생각할 수 있었다.) 변수 a 마저 같은 값이라니?

이건 인자 lpParam이 가리키는 주소의 변수가 1이 된 이후에 쓰레드 함수가 수행되었기 때문이다.

글을 쓰다보니 이건 문제의 시작일 뿐이라는 생각이 든다. 만약 for 루프가 완전히 끝난 시점에서 쓰레드 함수가 수행된다면?

인자로 넘어온 변수의 주소가 이미 사라져 버린 후였다면 문제는 더 컸을 것이다.

이것때문에 TLS라든지 GlobalHeap이라든지 하는 내용이 나온다고 생각된다(추측). 확언하지 못하는건 이것 때문이다.

우리에겐 전역 변수가 있는데 왜 그래? 동기만 잘 맞춰주면 되는거 아냐? new 로도 할수있고 malloc으로도 할수 있는데 왠 호들갑?

위의 글은 아직 짧은 나의 의견이고 TLS나 GlobalHeap에 대해서 더 알아보면 이해가 되리라 생각된다.

GDI+의 Image를 만들려고 IStorage를 생성하는 방법에서 출발한게 꼬리를 문다. 어차피 쓰레드에 들어가서 꼭 필요한게 메모리 관리다 보니 오히려 잘되었다. 톱니바퀴처럼 아귀를 잘 맞추어 보자.