ThreadPool、函数局部变量和 Thread 本地存储

ThreadPool, Function local variables and Thread local storage

提问人:Tharani B 提问时间:1/12/2023 最后编辑:Tharani B 更新时间:1/12/2023 访问量:290

问:

在线程池中,线程被重用以避免线程的创建和破坏。当线程池重用线程时,它不会清除线程本地存储中的数据。因此,当方法检查线程本地存储时,它找到的值是早期使用线程池线程时遗留的。

函数局部变量是函数的局部变量,而不是线程的局部变量。但是当我使用 boost::asio::thread_pool 创建线程池时,函数内部创建的局部变量的内存共享同一线程的相同内存。

因此,我有几个问题......

  1. 是存储在线程本地存储中的函数局部变量。是因为我为相同的线程获得了相同的内存吗?
  2. 对于按值传递的函数参数,同样的问题是什么?
  3. 在重用线程之前,有没有办法清除线程本地存储?

程序

#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <boost/asio/thread_pool.hpp>
#include <BoostThreading/UtilBoost.hpp>
#include <iostream>
boost::mutex mutex;
void my_task(int g)
{
    boost::lock_guard<boost::mutex> lock(mutex);
    int i=0;
    i++;
    std::cout<<boost::this_thread::get_id()<<" "<<&i<<" "<<i<<" "<<&g<<" "<<g<<"\n";
    SleepMs(30);
}
int main()
{
    boost::asio::thread_pool pool(4);
    for(int i=0;i<10;i++)
        boost::asio::post(pool, boost::bind(my_task,i));
    pool.join();
}

输出 Output image here

第一个地址是线程 id,第二个是在函数中创建的变量 i 的地址,第三个地址是通过传递值传递给my_task的变量 g 的地址。

C++ 线程池 boost-asio 线程本地存储

评论


答:

1赞 sehe 1/12/2023 #1
  1. 是存储在线程本地存储中的函数局部变量。是因为我为相同的线程获得了相同的内存吗?

函数局部变量位于堆栈上。

堆栈实际上始终(隐式)线程本地。这是 CPU 设计的直接结果。

唯一的例外可能是在上下文切换库(例如实现堆栈协程或纤程)时。

阅读这个绝对很棒的答案,了解堆栈分配和堆栈重用:局部变量的内存可以在其范围之外访问吗?

  1. 对于按值传递的函数参数,同样的问题是什么?

同样的答案。

  1. 在重用线程之前,有没有办法清除线程本地存储?

这实际上是一个有趣的问题。它概括为“C++ 中是否有包含具有自动存储持续时间的对象的安全擦除分配器?

不,那不存在。它可以是近似的,但它将涉及将所有基元类型包装成非平凡类型。在任务结束时使用显式函数主动擦除堆栈可能更有效。

上层框架必须保持原样,因为它们可能(并且将)包含正确线程管理所需的信息(例如,如果您使用它,它将包含允许线程中断的结构,线程本地存储的可移植仿真等)boost::threadboost::thread_specific_ptr

如果您需要这样的东西,请寻找一个围绕“安全擦除”的现有库,因为它非常容易出错。例如,一个朴素的调用可以(并且将)经常被编译器优化掉。memset