Правила и ограничения протокола 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;
    

    Поскольку объявление объектов C++, в которых используется атрибут thread, разрешено, следующие два примера семантически равнозначны.

    #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 до Windows Vista ключевое слово __declspec(thread) имеет некоторые ограничения. Если библиотека DLL объявляет любые данные или объекты как __declspec(поток), может возникнуть сбой защиты при динамической загрузке этих данных или объектов. После загрузки библиотеки DLL с помощью функции LoadLibrary сбой в системе происходит всякий раз, когда код ссылается на данные __declspec(поток). Пространство глобальных переменных для потока выделяется во время выполнения, поэтому размер данного пространства определяется исходя из расчетов требований приложения, а также требований всех статически компонуемых библиотек DLL. При использовании функции LoadLibrary не существует способа расширения этого пространства, чтобы разрешить объявление локальных переменных потока с ключевым словом __declspec (thread). В библиотеках DLL следует использовать API-функции TLS, такие как TlsAlloc, чтобы разместить TLS, если библиотека DLL загружается с помощью функции LoadLibrary.

См. также

Основные понятия

Локальное хранилище потока (TLS)