C++新特性 线程局部存储

简介: C++新特性 线程局部存储

本篇文章来介绍一下关于线程局部存储的一些概念

线程局部存储(Thread Local Storage,TLS)是一种编程技术,用于为每个线程提供独立的变量副本。它允许在多线程程序中创建全局变量的多个实例,每个实例都与特定的线程相关联。

传统上,全局变量是所有线程共享的,在并发环境下可能导致竞态条件和数据访问冲突。而通过使用线程局部存储,可以使每个线程都拥有自己独立的变量副本,从而避免了竞态条件问题。

线程局部存储通常用于以下情况:

  1. 线程特定数据:某些数据只对特定线程有意义,并且需要在线程内保持状态。
  2. 全局状态隔离:将全局状态分离为每个线程的私有副本,以提高并发性能。
  3. 线程上下文保存:将当前执行线程的上下文信息保存在特定于该线程的变量中。

在C++中,可以使用thread_local关键字来声明具有线程局部存储语义的变量。这样声明的变量将为每个执行线程维护一个独立的副本。

以下是代码实例:

#include <iostream>
#include <thread>
thread_local int tls_variable = 0;
void thread_function() {
    tls_variable += 1;
    std::cout << "Thread ID: " << std::this_thread::get_id() 
              << ", TLS variable: " << tls_variable << std::endl;
}
int main() {
    std::thread t1(thread_function);
    std::thread t2(thread_function);
    
    t1.join();
    t2.join();
    
    return 0;
}

上述示例中,每个线程都有自己独立的tls_variable副本。在线程函数内部,对该变量的修改只会影响到当前线程的副本。输出结果将显示每个线程对变量进行递增操作后的值。

需要注意的是,TLS仅在C++11及以上版本中可用,并且只能用于具有静态或线程存储期的变量,不能用于自动或动态分配的变量。

windows线程存储API:

在Windows平台上,可以使用以下API来实现线程局部存储(Thread Local Storage):

  1. TlsAlloc:用于分配一个TLS索引,返回一个TLS索引值。该索引将用作线程特定数据的唯一标识符。
  2. TlsFree:释放先前分配的TLS索引。
  3. TlsSetValue:设置当前线程中与给定TLS索引相关联的值。
  4. TlsGetValue:获取当前线程中与给定TLS索引相关联的值。

这四个函数的综合使用代码实例:

#include <iostream>
#include <windows.h>
DWORD tlsIndex;  // 全局变量用于保存TLS索引
void ThreadFunction() {
    int* threadData = new int(42);
    TlsSetValue(tlsIndex, threadData);  // 将线程特定数据存储到TLS中
    
    int* retrievedData = static_cast<int*>(TlsGetValue(tlsIndex));  // 从TLS中获取线程特定数据
    std::cout << "Thread ID: " << GetCurrentThreadId()
              << ", Thread-specific data: " << *retrievedData << std::endl;
    
    delete retrievedData;  // 释放内存
}
int main() {
    tlsIndex = TlsAlloc();  // 分配一个新的TLS索引
    if (tlsIndex == TLS_OUT_OF_INDEXES) {
        std::cerr << "Failed to allocate TLS index." << std::endl;
        return -1;
    }
    std::thread t1(ThreadFunction);
    std::thread t2(ThreadFunction);
    
    t1.join();
    t2.join();
    TlsFree(tlsIndex);  // 释放TLS索引
    return 0;
}

在上述示例中,使用TlsAlloc函数分配一个新的TLS索引,然后在每个线程的函数中使用TlsSetValue将线程特定数据存储到TLS中。在主函数中,创建两个线程,并通过调用TlsGetValue来检索并输出线程特定数据。最后,使用TlsFree释放TLS索引。

需要注意的是,上述代码仅为演示目的,并未考虑异常处理和内存泄漏等问题。实际应用中,请根据具体需求进行合适的错误处理和资源管理。

linux中完成线程局部存储的任务函数

在Linux中,可以使用线程局部存储(Thread Local Storage,TLS)来实现线程特定的数据。

#include <iostream>
#include <pthread.h>
// 定义线程局部数据键
pthread_key_t tls_key;
// 线程退出时的清理函数
void cleanup(void* value) {
    std::cout << "Cleaning up thread-local data: " << *(int*)value << std::endl;
    delete (int*)value;
}
// 线程函数
void* thread_func(void* arg) {
    // 创建一个动态分配的整数,并将其存储到线程局部存储中
    int* value = new int(42);
    pthread_setspecific(tls_key, value);
    // 获取线程局部存储中的值,并打印出来
    std::cout << "Thread-local value: " << *(int*)pthread_getspecific(tls_key) << std::endl;
    return nullptr;
}
int main() {
    // 创建线程局部数据键
    pthread_key_create(&tls_key, cleanup);
    // 创建多个线程并运行
    const int num_threads = 3;
    pthread_t threads[num_threads];
    
    for (int i = 0; i < num_threads; ++i) {
        pthread_create(&threads[i], nullptr, thread_func, nullptr);
    }
    // 等待所有线程结束并回收资源
    for (int i = 0; i < num_threads; ++i) {
        pthread_join(threads[i], nullptr);
    }
    // 销毁线程局部数据键
    pthread_key_delete(tls_key);
    return 0;
}

这段代码创建了一个线程局部存储键 tls_key并在每个线程中将不同的整数值存储到线程局部存储中。当每个线程结束时,会调用清理函数 cleanup 来释放动态分配的内存。

请注意,在编译时需要链接 pthread 库(使用 -lpthread 参数)。

linux实现线程存储的总结:

在Linux中,可以使用线程局部存储(Thread Local Storage,TLS)来实现线程特定的数据。以下是对线程局部任务存储函数的总结:

  1. 首先,需要包含头文件 pthread.h
  2. 使用 pthread_key_t 类型定义一个线程局部数据键。
  3. 定义一个清理函数,在线程退出时调用该函数来释放分配的内存。清理函数的参数类型为 void*
  4. 在主函数或其他适当位置创建线程局部数据键,使用 pthread_key_create() 函数。该函数接受两个参数:指向 pthread_key_t 的指针和清理函数指针。
  5. 创建并运行多个线程。在线程函数中,使用 pthread_setspecific() 函数将要存储的值与键关联起来。该函数接受两个参数:线程局部数据键和要存储的值。
  6. 若要访问已存储在线程局部存储中的值,可以使用 pthread_getspecific() 函数,并传入相应的线程局部数据键作为参数。
  7. 线程结束后,在清理函数中释放动态分配的内存或执行其他必要操作。
  8. 最后,在程序结束前或合适的地方销毁线程局部数据键,使用 pthread_key_delete() 函数进行销毁。

请注意,在编译时需要链接 pthread 库(使用 -lpthread 参数)。

这样就完成了在Linux上使用线程局部存储函数的过程。

好了本篇文章就描述到这里了 在这里小编想给大家推荐一个课程:

https://xxetb.xetslk.com/s/2PjJ3T

相关文章
|
2月前
|
编译器 程序员 定位技术
C++ 20新特性之Concepts
在C++ 20之前,我们在编写泛型代码时,模板参数的约束往往通过复杂的SFINAE(Substitution Failure Is Not An Error)策略或繁琐的Traits类来实现。这不仅难以阅读,也非常容易出错,导致很多程序员在提及泛型编程时,总是心有余悸、脊背发凉。 在没有引入Concepts之前,我们只能依靠经验和技巧来解读编译器给出的错误信息,很容易陷入“类型迷路”。这就好比在没有GPS导航的年代,我们依靠复杂的地图和模糊的方向指示去一个陌生的地点,很容易迷路。而Concepts的引入,就像是给C++的模板系统安装了一个GPS导航仪
133 59
|
1月前
|
安全 编译器 C++
【C++11】新特性
`C++11`是2011年发布的`C++`重要版本,引入了约140个新特性和600个缺陷修复。其中,列表初始化(List Initialization)提供了一种更统一、更灵活和更安全的初始化方式,支持内置类型和满足特定条件的自定义类型。此外,`C++11`还引入了`auto`关键字用于自动类型推导,简化了复杂类型的声明,提高了代码的可读性和可维护性。`decltype`则用于根据表达式推导类型,增强了编译时类型检查的能力,特别适用于模板和泛型编程。
24 2
|
1月前
|
缓存 安全 C++
C++无锁队列:解锁多线程编程新境界
【10月更文挑战第27天】
61 7
|
1月前
|
消息中间件 存储 安全
|
2月前
|
算法 NoSQL Java
Springboot3新特性:GraalVM Native Image Support和虚拟线程(从入门到精通)
这篇文章介绍了Spring Boot 3中GraalVM Native Image Support的新特性,提供了将Spring Boot Web项目转换为可执行文件的步骤,并探讨了虚拟线程在Spring Boot中的使用,包括如何配置和启动虚拟线程支持。
138 9
Springboot3新特性:GraalVM Native Image Support和虚拟线程(从入门到精通)
|
2月前
|
存储 消息中间件 资源调度
C++ 多线程之初识多线程
这篇文章介绍了C++多线程的基本概念,包括进程和线程的定义、并发的实现方式,以及如何在C++中创建和管理线程,包括使用`std::thread`库、线程的join和detach方法,并通过示例代码展示了如何创建和使用多线程。
60 1
|
2月前
|
存储 并行计算 安全
C++多线程应用
【10月更文挑战第29天】C++ 中的多线程应用广泛,常见场景包括并行计算、网络编程中的并发服务器和图形用户界面(GUI)应用。通过多线程可以显著提升计算速度和响应能力。示例代码展示了如何使用 `pthread` 库创建和管理线程。注意事项包括数据同步与互斥、线程间通信和线程安全的类设计,以确保程序的正确性和稳定性。
|
2月前
|
存储 前端开发 C++
C++ 多线程之带返回值的线程处理函数
这篇文章介绍了在C++中使用`async`函数、`packaged_task`和`promise`三种方法来创建带返回值的线程处理函数。
83 6
|
2月前
|
缓存 负载均衡 Java
c++写高性能的任务流线程池(万字详解!)
本文介绍了一种高性能的任务流线程池设计,涵盖多种优化机制。首先介绍了Work Steal机制,通过任务偷窃提高资源利用率。接着讨论了优先级任务,使不同优先级的任务得到合理调度。然后提出了缓存机制,通过环形缓存队列提升程序负载能力。Local Thread机制则通过预先创建线程减少创建和销毁线程的开销。Lock Free机制进一步减少了锁的竞争。容量动态调整机制根据任务负载动态调整线程数量。批量处理机制提高了任务处理效率。此外,还介绍了负载均衡、避免等待、预测优化、减少复制等策略。最后,任务组的设计便于管理和复用多任务。整体设计旨在提升线程池的性能和稳定性。
87 5
|
2月前
|
C++
C++ 多线程之线程管理函数
这篇文章介绍了C++中多线程编程的几个关键函数,包括获取线程ID的`get_id()`,延时函数`sleep_for()`,线程让步函数`yield()`,以及阻塞线程直到指定时间的`sleep_until()`。
45 0