c – 静态变量和线程局部存储

前端之家收集整理的这篇文章主要介绍了c – 静态变量和线程局部存储前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
背景:

我发现了一些有趣的边缘情况,涉及跨多个线程的静态内存初始化.具体来说,我使用的是Howard Hinnant的TZ库,它在许多不同的线程中对我的其余代码工作正常.

现在,我正在开发一个依赖于另一个线程和条件变量的日志类.不幸的是,当我尝试使用date :: make_zoned(data :: locate_zone(“UTC”),tp)格式化chrono time_point时,库崩溃了.在挖掘tz.cpp后,我发现内部返回的时区数据库正在评估为NULL.这一切都来自以下代码段:

tzdb_list&
get_tzdb_list()
{
    static tzdb_list tz_db = create_tzdb();
    return tz_db;
}

可以看出,数据库列表是静态存储的.使用一些printf()和一些时间使用GDB我可以看到从主线程多次调用返回相同的db,但是从我的记录器线程调用时返回NULL.

但是,如果我将tzdb_list的声明更改为:

static thread_local tzdb_list tz_db = create_tzdb();

一切都按预期工作.这并不奇怪,因为thread_local将导致每个线程完成创建tzdb_list的独立实例的繁重工作.显然,这会浪费内存,以后很容易引起问题.因此,我真的不认为这是一个可行的解决方案.

问题:

>一个线程与另一个线程的调用会导致静态内存的行为有何不同?如果有的话,我会期望与正在发生的事情相反(例如,线程在初始化内存上“争夺”;没有一个接收到NULL指针).
>返回的静态引用如何首先有多个不同的值(在我的例子中,有效内存与NULL)?
>随着thread_local内置到库中,我在可寻址区域的两端获得了截然不同的内存位置;为什么?我怀疑这与线程内存分配的位置与主进程内存有关,但不知道线程分配区域的确切细节.

参考:

我的日志记录线程创建时间:

outputThread = std::thread(Logger::outputHandler,&outputQueue);

而实际的输出处理程序/库的调用(LogMessage只是std :: tuple的typedef):

void Logger::outputHandler(LogQueue *queue)
{
    LogMessage entry;
    std::stringstream ss;

    while (1)
    {
        queue->pop(entry);           // Blocks on a condition variable

        ss << date::make_zoned(date::locate_zone("UTC"),std::get<0>(entry))
           << ":" << levelId[std::get<1>(entry)
           << ":" << std::get<3>(entry) << std::endl;

        // Printing stuff

        ss.str("");
        ss.clear();
    }
}

可根据要求提供其他代码输出样本.

编辑1

这绝对是我的代码中的一个问题.当我删除所有内容时,我的记录器按预期工作.对我来说奇怪的是,我在完整应用程序中的测试用例只是在main中打印两次,在手动退出之前调用logger.其余的应用程序初始化都没有运行,但我在此时链接所有支持库(Microsoft CPP REST SDK,MysqL Connector for C和Howard的日期库(静态)).

我很容易看到有什么东西可以踩踏这个内存但是,即使在我的应用程序中的“完整”情况下,我也不知道为什么主线程上的打印会起作用,但下一行调用记录器会失败.如果在初始阶段横向发生某些事情,我希望所有的电话都能打破.

我还注意到,如果我使记录器保持静态,问题就会消失.当然,这会改变内存布局,因此不排除堆/堆栈粉碎.我觉得有趣的是我可以在main()的开头全局或堆栈上声明记录器,并且两者都会以相同的方式进行段错误.但是,如果我将logger声明为static,则全局和基于堆栈的声明都可以正常工作.

仍然试图创建一个再现这个的最小测试用例.

我已经用-lpthread链接了;自从这个应用程序开始以来已经非常多了.

操作系统是在Intel Xeon上运行的Fedora 27 x86_64.编译:

$g++ --version
g++ (GCC) 7.3.1 20180130 (Red Hat 7.3.1-2)
Copyright (C) 2017 Free Software Foundation,Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

解决方法

看来这个问题是由 bug in tz.cpp which has since been fixed引起的.

错误是有一个命名空间范围变量,其初始化不能以正确的顺序保证.通过将该变量转换为函数本地静态来确定正确的初始化顺序,从而解决了这个问题.

我向所有可能受到这个bug影响的人道歉.我要感谢所有报道过的人.

猜你在找的C&C++相关文章