TLS 규칙 및 제한
정적으로 바인딩된 스레드 지역 개체 및 변수를 선언하는 경우 다음과 같은 지침을 준수해야 합니다.
thread 특성은 데이터 선언 및 정의에만 적용할 수 있습니다. 함수 선언 또는 정의에는 사용할 수 없습니다. 예를 들어, 다음 코드는 컴파일러 오류를 생성합니다.
#define Thread __declspec( thread ) Thread void func(); // This will generate an error.
thread 한정자는 static 범위를 사용하는 데이터 항목에만 지정할 수 있습니다. 여기에는 전역 데이터 개체(static 및 extern), 지역 정적 개체 및 C++ 클래스의 정적 데이터 멤버가 포함됩니다. 자동 데이터 개체는 thread 특성을 사용하여 선언할 수 없습니다. 다음 코드에서는 컴파일러 오류가 생성됩니다.
#define Thread __declspec( thread ) void func1() { Thread int tls_i; // This will generate an error. } int func2( Thread int tls_i ) // This will generate an error. { return tls_i; }
스레드 지역 개체의 선언과 정의에서 thread 특성을 모두 지정해야 합니다. 예를 들어, 다음 코드는 오류를 생성합니다.
#define Thread __declspec( thread ) extern int tls_i; // This will generate an error, since the int Thread tls_i; // declaration and definition differ.
thread 특성은 형식 한정자로 사용할 수 없습니다. 예를 들어, 다음 코드는 컴파일러 오류를 생성합니다.
char __declspec( thread ) *ch; // Error
C++ 클래스는 thread 특성을 사용할 수 없습니다. 그러나 C++ 클래스 개체는 thread 특성을 사용하여 인스턴스화할 수 있습니다. 예를 들어, 다음 코드는 컴파일러 오류를 생성합니다.
#define Thread __declspec( thread ) class Thread C // Error: classes cannot be declared Thread. { // Code }; C CObject;
thread 특성을 사용하는 C++ 개체를 선언할 수 있으므로 다음 두 예제는 같은 의미입니다.
#define Thread __declspec( thread ) Thread class B { // Code } BObject; // OK--BObject is declared thread local. class B { // Code }; Thread B BObject; // OK--BObject is declared thread local.
스레드 지역 개체의 주소는 상수로 간주되지 않으므로 이 주소를 포함하는 모든 식은 상수 식으로 간주되지 않습니다. 표준 C에서 이것은 스레드 지역 변수의 주소를 개체 또는 포인터의 초기 값으로 사용할 수 없음을 의미합니다. 예를 들어, 다음 코드는 C 컴파일러에서 오류로 간주됩니다.
#define Thread __declspec( thread ) Thread int tls_i; int *p = &tls_i; //This will generate an error in C.
C++에서는 이러한 제한이 적용되지 않습니다. C++에서는 모든 개체를 동적으로 초기화할 수 있으므로 스레드 지역 변수의 주소를 사용하는 식으로 개체를 초기화할 수 있습니다. 이 작업은 스레드 지역 개체 생성과 동일한 방식으로 수행됩니다. 예를 들어, 앞의 코드는 C++ 소스 파일로 컴파일할 경우 오류를 생성하지 않습니다. 스레드 지역 변수의 주소는 주소를 사용하는 스레드가 있는 경우에만 유효합니다.
표준 C에서는 비 정적 범위의 개체에 한해 자신에 대한 참조를 포함하는 식으로 개체 또는 변수를 초기화할 수 있습니다. C++는 일반적으로 자신에 대한 참조를 포함하는 식에서 개체의 동적 초기화를 허용하지만 스레드 지역 개체에 대해서는 이러한 종류의 초기화를 허용하지 않습니다. 예를 들면 다음과 같습니다.
#define Thread __declspec( thread ) Thread int tls_i = tls_i; // Error in C and C++ int j = j; // OK in C++, error in C Thread int tls_i = sizeof( tls_i ) // Legal in C and C++
초기화되는 개체를 포함하는 sizeof 식은 자신에 대한 참조를 나타내지 않으며 C와 C++ 모두에서 사용됩니다.
앞으로 스레드 로컬 저장소 기능이 향상될 수 있으므로 C++는 스레드 데이터의 동적 초기화를 허용하지 않습니다.
Windows Vista 이전의 Windows 운영 체제에서는 __declspec(스레드)에 일부 제한이 있습니다. DLL에서 데이터 또는 개체를 __declspec(스레드)로 선언하는 경우 동적으로 로드되면 보호 결함이 발생할 수 있습니다. LoadLibrary로 DLL이 로드된 다음 코드에서 __declspec(스레드) 데이터를 참조할 때마다 시스템 오류가 발생합니다. 스레드의 전역 변수 공간은 런타임에서 할당되므로 이 공간의 크기는 응용 프로그램의 요구 사항과 정적으로 링크된 모든 DLL의 요구 사항을 더한 값을 기반으로 합니다. LoadLibrary를 사용하는 경우 __declspec(스레드)로 선언된 스레드 지역 변수를 허용할 만큼 이 공간을 확장할 수 없습니다. LoadLibrary를 사용하여 DLL을 로드하는 경우 TLS를 할당하려면 DLL에서 TLS API(예: TlsAlloc)를 사용하십시오.