C++ thread_local 关键字

thread_local 是 C++11 引入的一个关键字,它的主要作用是用于声明线程私有变量,该变量在线程内部的多个函数之间可以共享,在线程和线程之间则是独立的、不共享的。

1. 问题场景

局部变量可以在线程之间独立,在线程内部则生命周期只在某个函数内部。如果想多个线程关联函数访问的话,就必须使用全局变量,但是又会带来资源竞争问。另外,局部变量无法保持函数执行的状态,因为函数执行结束之后就会被销毁。如果使用静态局部变量,在多个线程中中该变量也会带来资源竞争问题。

也就是说,我们之前定义的变量在多线程场景下,会导致变量被多个线程共享,线程无法独享。所以,C++11 专门引入一个关键字thread_local 来给线程定义具有线程生命周期的变量。

在单线程程序中,可以根据使用场景选择合适的变量。例如:

  • 需要在函数内部独立使用的数据,可以使用局部变量
  • 需要跨函数使用的数据,可以使用全局变量
  • 需要在函数执行过程中持续保存的数据,可以使用静态局部变量

然而,当进入多线程场景时,这些针对单线程的变量策略可能会显得不够适用,具体表现如下:

  • 需要跨线程函数使用的数据,全局变量会出现资源竞争问题
  • 如果线程运行期间需要保存一些信息,静态局部变量又是线程共享的,也可能会导致资源竞争问题

即:哪些具有整个程序生命周期的变量在多线程的场景下,会出现资源竞争问题。所以,我们就想能不能把这些变量的生命周期给压缩到线程的生周期内,这样线程内部可以安全使用,线程之间也不会冲突。这就是 thread_local 出现的目的。

#include<iostream>
#include<thread>
#include<mutex>

std::mutex mtx;

// 全局变量可以实现跨函数使用,但是线程并不独立
int num = 10;

void step1() { num += 1; }
void step2() { num += 2; }

void do_work()
{
    step1();
    step2();

    std::lock_guard<std::mutex> lock(mtx);
    std::cout << "thread:" << std::this_thread::get_id() << " num:" << num << std::endl;
}

void test()
{
    std::thread t1(do_work);
    std::thread t2(do_work);
    t1.join();
    t2.join();
}

局部变量的局限性

希望在线程运行期间能够持续存储一些信息。由于普通局部变量的生命周期仅限于函数调用期间,函数结束后会被销毁。而静态局部变量又是线程共享。所以在当前场景下就不太合适。

#include <iostream>
#include <thread>
#include<mutex>

std::mutex mtx;

void counter()
{
    // 局部变量函数作用域,函数结束会被回收
    // int val = 10;
    // 静态变量多线程共享,变量不能线程独立
    static int val = 10;
    val += 1;
    std::lock_guard<std::mutex> lock(mtx);
    std::cout << "thread " << std::this_thread::get_id() << ": val = " << val << std::endl;
}

void do_work()
{
    for (int i = 0; i < 5; ++i)
    {
        std::this_thread::sleep_for(std::chrono::milliseconds(500));
        counter();
    }
}

void test()
{
    std::thread t1(do_work);
    std::thread t2(do_work);
    t1.join();
    t2.join();
}

简言之:在某些场景下,我们需要给线程单独去创建数据,线程内共享,线程之间独立,并且线程结束自动回收。这就是线程局部存储 TLS 要解决的问题。

2. 用法

使用 thread_local 可以声明一个变量,使其在每个线程中都有独立的副本。线程结束时,这些局部变量会被销毁。thread_local 变量的作用域和普通变量相同,只是在不同的线程中,每个线程都会有独立的存储空间。例如,线程局部变量可以是函数内的局部变量类的成员变量,甚至是全局变量

未经允许不得转载:一亩三分地 » C++ thread_local 关键字
评论 (0)

2 + 9 =