C++之thread_local

C++之thread_local



前言

thread_local是C++11引入的关键字,该关键字会影响一个变量的存储周期。本文简单介绍C++的存储周期以及该关键字带来的影响。

1.C++的存储周期

C++中的存储周期是指变量或者对象存在的时期,也就是说一个对象或者变量从创建到销毁的时间跨度。存储周期与作用域有关,但是二者不等同。对于一个作用域来说,主要描述变量在何处可以被访问,存储周期则描述了变量何时存在,C++的存储周期分为四类

1.1静态存储周期(Static storage duration)

具有静态存储周期的对象在其程序的整个运行期间都会存在,知道程序退出位置。静态存储周期的对象通常在程序启动时创建并在结束时销毁,这类对象包括全局变量,静态局部变量以及带有static存储类的对象。
该类对象的特性如下:

  • 程序运行期间始终存在
  • 通常在全局数据区分配内存
  • 静态局部变量在函数每次调用时,都会保持上次调用的状态
  • 除非作用域限制,全局变量在整个程序中可被访问

1.2 动态存储周期(Dynamic storage duration)

这类对象或者变量是在程序运行时通过动态内存分配函数创建的,例如使用new或者malloc。这些对象的声明周期由程序员通过嗲用相应的释放内存函数来控制,如deletefree
该类对象特性如下:

  • 生命周期由程序员控制
  • 通常在堆区分配内存
  • 需要手动管理内存分配释放

1.3 自动存储周期(Automatic storage duration)

具有自动存储周期的对象在其作用域内存在,一旦离开作用域就会被自动销毁,这类对象通常是在函数体或者复合语句中声明的局部变量。
这类对象特性如下:

  • 在进入作用域时创建,离开作用域时被销毁
  • 通常在栈区分配内存
  • 大部分情况下生命周期和所在函数的声明周期保持一致

1.4 线程存储周期(thread local storage duration)

在C++11中引入,允许多线程程序中的每个线程拥有自己单独的变量实例,这些变量的声明周期通常和所属的线程的声明周期一样长。
这类对象特性如下:

  • 从初始化开始到线程终止结束
  • 在线程范围级别具有可见性

2.thread_local

有且只有thread_local关键字修饰的变量会具有线程存储周期。如前文所述,在线程开始的时候被生成,在线程结束的时候被销毁。
该类对象具有如下特性:

  • 线程安全性:每个线程拥有自己独立的副本,因此对thread_local关键字修饰的变量进行操作不需要加锁
  • 声明周期:线程局部变量的生命周期与其所属的线程的生命周期相同。当线程创建时,变量的副本被创建;当线程终止时,副本被销毁。
#include <iostream>
#include <thread>

thread_local int thread_count = 0;

void increment_and_print() {
    ++thread_count;
    std::cout << "Thread " << std::this_thread::get_id() << " count: " << thread_count << std::endl;
}

int main() {
    std::thread t1(increment_and_print);
    std::thread t2(increment_and_print);

    t1.join();
    t2.join();

    return 0;
}

上面的是一个示例的C++程序,定义了一个函数increment_and_print对线程变量进行自增操作,分别创建两个线程调用该函数,执行结果如下所示:
在这里插入图片描述

可以看到对于不同的线程来说,分别拥有一个变量副本,在各自的线程中分别实现了+1的自增操作。

#include <iostream>
#include <thread>

class share
{
public:
    void addself()
    {
        thread_local int thread_count = 0;
        thread_count++;
        std::cout << "count: " << thread_count << std::endl;
    }
};

void increment_and_print() {
    share s1;
    share s2;
    s1.addself();
    s2.addself();
}

int main() {
    std::thread t1(increment_and_print);
    t1.join();

    return 0;
}

这里还有一段比较有意思的程序,该程序声明了一个类,并且该类的成员函数里存在一个thread_local关键字修饰的变量,并对其完成了初始化为0和自增的操作。
在下面定义的线程函数里,实例化了两个对象,分别调用各自的自增函数,输出结果如下:
在这里插入图片描述

结果分别是1和2,并非我们设想的1和1,这是因为类的成员函数内定义的 thread_local 变量,同一个线程内的该类的多个对象都会共享一个变量实例,并且只会在第一次执行这个成员函数时初始化这个变量实例,类似于类的静态成员变量。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值