c++ 线程局部变量thread_local

本文深入探讨C++11引入的线程局部变量特性,详细解释thread_local关键字如何实现每个线程拥有独立的变量实例,避免资源竞争,提高并发性能。并提供了具体的代码示例和Linux线程局部存储的底层实现原理。

    c++11 中添加了新的关键字thread_local,用来声明新的存储期(线程存储期变量),即线程局部变量。

   存储类指定符是名称声明语法的 decl-specifier-seq 的一部分。与名称的作用域一同,它们控制名称的二个独立属性:其“存储期”与其“链接”。auto - 自动存储期(C++11 起)。register - 自动存储期。亦提示编译器将此对象置于处理器的寄存器。C++17 前(弃用),static - 静态或线程存储期和内部链接。extern - 静态或线程存储期和外部链接,thread_local - 线程存储期。

 存储期
程序中的所有对象拥有下列存储期之一:
1 自动存储期。对象的存储在外围代码块开始时分配,而在结束时解分配。除了声明为 static 、 extern 或 thread_local 的所有局部对象拥有此存储期。
2 静态存储期。对象的存储在程序开始时分配,而在程序结束时解分配。只存在对象的一个实例。所有声明于命名空间作用域(包含全局命名空间)的对象,加上声明带有 static 或 extern 的对象拥有此存储期。
3 线程存储期。对象的存储在线程开始时分配,而在线程结束时解分配。每个线程拥有其自身的对象实例。唯有声明为 thread_local 的对象拥有此存储期。 thread_local 能与 static 或 extern 一同出现,以调整链接。
4 (C++11 起)动态存储期。通过使用动态内存分配函数,由请求分配和解分配对象的存储。

     什么是thread_local,关于thread_local。thread_specific_ptr代表了一个全局的变量,而在每个线程中都各自new一个线程本地的对象交给它进行管理,这样,各个线程就可以各自独立地访问这个全局变量的本地存储版本,线程之间就不会因为访问同一全局对象而引起资源竞争导致性能下降。而线程结束时,这个资源会被自动释放。 C++11的thread_local来自于boost thread_specific_ptr。

#include <iostream>
#include <string>
#include <thread>
#include <mutex>
 
thread_local unsigned int rage = 1; 
std::mutex cout_mutex;
 
void increase_rage(const std::string& thread_name)
{
    ++rage; // 在锁外修改 OK ;这是线程局域变量
    std::lock_guard<std::mutex> lock(cout_mutex);
    std::cout << "Rage counter for " << thread_name << ": " << rage << '\n';
}
 
int main()
{
    std::thread a(increase_rage, "a"), b(increase_rage, "b");
 
    {
        std::lock_guard<std::mutex> lock(cout_mutex);
        std::cout << "Rage counter for main: " << rage << '\n';
    }
 
    a.join();
    b.join();
}

/*
Rage counter for a: 2
Rage counter for main: 1
Rage counter for b: 2
*/

         我们可以看到,声明为线程存储期的变量,每个线程之间互不影响,通俗的讲我们可以认为每个线程拥有此变量的一个副本,每个线程之前互不影响,其底层的原理Linux中的线程局部存储等提供的api接口。

#include <pthread.h>

// Returns 0 on success, or a positive error number on error
int pthread_key_create (pthread_key_t *key, void (*destructor)(void *));

// Returns 0 on success, or a positive error number on error
int pthread_key_delete (pthread_key_t key);

// Returns 0 on success, or a positive error number on error
int pthread_setspecific (pthread_key_t key, const void *value);

// Returns pointer, or NULL if no thread-specific data is associated with key
void *pthread_getspecific (pthread_key_t key);

应用实例:利用thread_local实现可重入锁

操作系统如何保证每个线程独享一份变量的数据呢?

 Linux中的线程局部存储(一)   Linux中的线程局部存储(二)

扩展知识:java中同样有一个ThreadLoca类实现了类似的功能,但是其实现却完全不同:

ThreadLocal-面试必问深度解析   ThreadLocal源码分析(JDK8)

C++11之前,标准库没有提供像`thread_local`这样统一的方式来定义线程局部变量,但可以通过操作系统提供的API来实现类似的功能。以常见的操作系统为例,有以下两种实现方式: ### POSIX线程库(pthread) 在基于POSIX标准的系统(如Linux、macOS等)中,可以使用`pthread_key_create`、`pthread_setspecific`和`pthread_getspecific`等函数来实现线程局部存储。 ```cpp #include <iostream> #include <pthread.h> // 定义一个线程局部存储的键 pthread_key_t tls_key; // 线程函数 void* thread_function(void* arg) { // 为当前线程设置线程局部变量的值 int* value = new int(42); pthread_setspecific(tls_key, value); // 获取当前线程线程局部变量的值 int* retrieved_value = static_cast<int*>(pthread_getspecific(tls_key)); std::cout << "Thread " << pthread_self() << " value: " << *retrieved_value << std::endl; // 释放内存 delete retrieved_value; return nullptr; } int main() { // 创建线程局部存储的键 pthread_key_create(&tls_key, nullptr); // 创建两个线程 pthread_t threads[2]; for (int i = 0; i < 2; ++i) { pthread_create(&threads[i], nullptr, thread_function, nullptr); } // 等待线程结束 for (int i = 0; i < 2; ++i) { pthread_join(threads[i], nullptr); } // 删除线程局部存储的键 pthread_key_delete(tls_key); return 0; } ``` ### Windows API 在Windows系统中,可以使用`TlsAlloc`、`TlsSetValue`和`TlsGetValue`等函数来实现线程局部存储。 ```cpp #include <iostream> #include <windows.h> // 定义一个线程局部存储的索引 DWORD tls_index; // 线程函数 DWORD WINAPI thread_function(LPVOID lpParam) { // 为当前线程设置线程局部变量的值 int* value = new int(42); TlsSetValue(tls_index, value); // 获取当前线程线程局部变量的值 int* retrieved_value = static_cast<int*>(TlsGetValue(tls_index)); std::cout << "Thread " << GetCurrentThreadId() << " value: " << *retrieved_value << std::endl; // 释放内存 delete retrieved_value; return 0; } int main() { // 分配一个线程局部存储的索引 tls_index = TlsAlloc(); // 创建两个线程 HANDLE threads[2]; for (int i = 0; i < 2; ++i) { threads[i] = CreateThread(nullptr, 0, thread_function, nullptr, 0, nullptr); } // 等待线程结束 WaitForMultipleObjects(2, threads, TRUE, INFINITE); // 关闭线程句柄 for (int i = 0; i < 2; ++i) { CloseHandle(threads[i]); } // 释放线程局部存储的索引 TlsFree(tls_index); return 0; } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值