# 线程局部存储

[English]

## 概述

线程局部存储 (TLS) 机制可以分配变量，使每个现有的线程都有相应变量实例。ESP-IDF 提供了以下三种方法，支持使用此类变量：

- FreeRTOS 原生 API：ESP-IDF FreeRTOS 原生 API。

- Pthread API：ESP-IDF pthread API。

- C11 标准：C11 标准引入了特殊关键字，将变量声明为线程局部变量。

## FreeRTOS 原生 API

ESP-IDF FreeRTOS 提供以下 API，用于管理线程局部变量：

- `vTaskSetThreadLocalStoragePointer()`

- `pvTaskGetThreadLocalStoragePointer()`

- `vTaskSetThreadLocalStoragePointerAndDelCallback()`

此时，可以分配的最大变量数量受 CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS 限制。变量保存在任务控制块 (TCB) 中，并由其索引访问。注意，索引 0 保留，供 ESP-IDF 内部使用。

通过使用上述 API，你可以分配任意大小的线程局部变量，并将其分配给任意数量的任务。不同任务可以拥有不同的 TLS 变量集。

如果变量大小超过 4 个字节，你需要负责为其分配/释放内存。在任务删除时，FreeRTOS 会释放变量，但必须提供回调函数来适当清理。

## Pthread API

ESP-IDF 提供以下 POSIX 支持（包括 POSIX 线程支持），用于管理线程局部变量：

- `pthread_key_create()`

- `pthread_key_delete()`

- `pthread_getspecific()`

- `pthread_setspecific()`

Pthread API 具备 FreeRTOS 原生 API 的所有优点，并突破了 FreeRTOS 原生 API 的部分限制，如变量数量仅受堆上可用内存大小的限制。然而由于 Pthread API 具备动态性质，与原生 API 相比，这个 API 引入了额外的性能开销。

## C11 标准

ESP-IDF FreeRTOS 支持基于 C11 标准的线程局部变量，使用 `__thread` 关键字指定。要了解 GCC 功能详情，请参阅 GCC 文档。

这类变量的存储空间分配在任务栈上。请注意，即使任务根本不使用这些变量，程序中所有这类变量的存储区域都会在系统中每个任务的栈上分配。例如，ESP-IDF 系统任务（如 `ipc`、`timer` 任务等）也有额外的栈空间分配。因此，使用这个特性时，需要谨慎考虑。

在使用 C11 线程局部变量时，可以针对以下特点进行权衡：在编程中使用 C11 线程局部变量非常方便，使用最少的 CPU 指令即可访问，但这一优势的代价是系统中所有任务都会额外使用栈。由于变量分配的静态性质，系统中的所有任务具有一组相同的 C11 线程局部变量。

此文档对您有帮助吗？

- 反馈已收到，谢谢！
- 如果您有其他意见，欢迎填写乐鑫文档反馈表。

- 我们重视您的反馈。
- 您可以填写乐鑫文档反馈表告诉我们如何改进该文档。