C++:处理线程本地对象销毁

2023-12-27

我有一个日志系统,它基本上使用线程本地缓冲区来记录。这有助于减少锁定。可以将一堆消息写入线程本地缓冲区并一次性刷新。而且由于它是线程本地的,我们可以避免为每个日志消息分配缓冲区。

无论如何,问题是在进程退出期间。我们在访问线程本地缓冲区时看到崩溃。

我拥有的线程本地对象类似于std::vector<Buffer>. [vector因为有多个缓冲区]。代表性的代码是这样的。

Buffer* getBuffer (int index)
{
    static thread_local auto buffers =    std::make_unique<std::vector<Buffer>>();
    return buffers ? buffers->at(index) : nullptr;
}

现在,当程序退出并调用全局析构函数时,不幸的是其中一些会记录。析构函数是从主线程调用的(否则不执行任何操作)。因此,当第一个全局对象被销毁并调用记录器时,会创建 thread_local 缓冲区,但它会立即被销毁,因为对象以与创建相反的顺序被销毁,并且这是创建的最后一个静态对象。当下一个全局对象析构函数调用记录器时,它正在有效地访问被破坏的对象,我认为这就是问题所在。

但我查看了 unique_ptr 析构函数,它确实将其内部的指针设置为 nullptr [或者至少将指针设置为默认构造的指针 - 我相信其值初始化为零??]。所以我的return buffers ? buffers->at(index) : nullptr;检查应该阻止访问已释放的对象,不是吗?

我创建了一个玩具程序来尝试这个,我发现buffers ?检查确实阻止了访问。但在真实的代码库中,这种情况并没有发生。在崩溃时,访问的向量已经是烤面包了。

现在,如果有人能告诉我一个神奇的解决方案,它会让我的生活变得轻松:-)。否则任何想法为什么bool的运算符unique_ptr不返回false。是因为它是访问被破坏的对象的经典未定义行为吗?

我在堆栈溢出中读到,如果对象有一个简单的析构函数,则可以在销毁后访问它。在这种情况下,如果我创建一个线程本地的,我的问题就解决了bool就在上面unique_ptr,并在包含的包装类的析构函数中将其设置为 trueunique_ptr?


但我查看了 unique_ptr 析构函数,它确实将其中的指针设置为 nullptr

不要紧。一旦对象的生命周期结束,以任何方式访问该对象都是UB。所以这是行不通的。

从寿命角度看问题

问题。

您的全局日志在某些本地缓冲区之前超出了范围。

解决方案

全局日志的寿命必须比本地缓冲区的寿命长。

如何实现

如果全局日志的生存时间必须比缓冲区长,则必须首先创建它。要强制执行此操作,请确保您的本地缓冲区在构造时请求对全局缓冲区的引用。这将强制首先创建全局日志,从而在本地缓冲区被破坏时保持活动状态。

示例解决方案

像这样的东西:

class Log
{
    public:
        static Log& getLog()
        {
            static Log theOneAndOnlyLog;
            return theOneAndOnlyLog;
        }
    }
};

class BufferFrame
{
    std::vector<Buffer>   buffer;
    BufferFrame()
    {
        Log::getLog();   // Force the log to be created first.
                         // Note: Order of destruction is guranteed
                         //       for static storage duration objects
                         //       to be the exact reverse of the order of
                         //       creation.
                         //
                         // This means if A is created before B
                         // Then B must be destroyed before A
                         //
                         // Here we know that `theOneAndOnlyLog`
                         // has been constructed (fully) thus `this`
                         // object is created after it. Thus this object
                         // will be destroyed before `theOneAndOnlyLog`.
                         //
                         // This means you can safely accesses `theOneAndOnlyLog`
                         // from the destructor of this object.
    }
    ~BufferFrame()
    {
        // We know the log has been created first
        // So we know it is still alive now.
        foreach(Buffer& buf: buffer) {
             Log::getLog() << buf; // Dump Buffer
        }
    }
    Buffer& at(std::size_t index)
    {
        return buffer.at(index);
    }
};
Buffer& getBuffer(int index)
{
    static thread_local BufferFrame buffers;
    return buffers.at(index);  // Note this will throw if index is out of range.
}

class MyObjectThatLogsToBuffer
{
    public:
        MyObjectThatLogsToBuffer()
        {
            getBuffer(0);   // Have created the FramBuffer
                            // It is created first. So it will be
                            // destroyed after me. So it is safe to
                            // access in destructor.
        }
        ~MyObjectThatLogsToBuffer()
        {
            log("I am destroyed");  // assume this calls getBuffer()
        }                           // while logging. Then it will work.
};
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++:处理线程本地对象销毁 的相关文章

随机推荐

  • 文本框大小调整时窗口大小也随之调整

    这是我的窗口中可用的文本框代码 form1 xaml 我的要求是当我调整窗口大小时我也想调整文本框宽度 我怎样才能实现这一点
  • 在 Bash 中,如何删除目录中文件名中的所有数字,同时保持文件扩展名不变

    我的目录中有这样的文件 asdfs54345gsdf pdf gsdf6456wer pdf oirt4534724wefd pdf 我想将所有文件重命名为数字 pdf 因此上述文件将重命名为 54345 pdf 6456 pdf 4534
  • 预测失败:内容必须是标量

    我已成功训练 导出并上传我的 retrained graph pb 到 ML Engine 我的导出脚本如下 import tensorflow as tf from tensorflow python saved model import
  • 不区分大小写

    If TextBox2 Text a AndAlso TextBox21 Text a Then MessageBox Show A totCorrect totCorrect corAns ElseIf TextBox2 Text b A
  • 如何实现通用 Kafka Streams 反序列化器

    我喜欢 Kafka 但讨厌编写大量序列化器 反序列化器 所以我尝试创建一个GenericDeserializer
  • Tk 树视图列排序

    有没有办法对 a 中的条目进行排序Tk 树视图 http www tkdocs com tutorial tree html通过单击该列 令人惊讶的是 我找不到任何相关文档 教程 帕特霍伊茨 http www patthoyts tk fr
  • .htaccess 中的 RewriteCond 和 RewriteRule

    我有一个客户端文件夹位于http www example com client http www example com client但是 我现在已经在服务器上安装了 SSL 并且想要使用 HTACCESS 添加永久重定向 以便每当访问 c
  • Swagger UI 中的掩码输入(示例 - 密码)?

    我有一个 API 的三个路径变量 我想用 屏蔽 Swagger UI 上的一个输入 使用 Springdoc OpenAPI 时如何做到这一点 您只需使用 swagger 注释即可 Parameter schema Schema type
  • 有什么理由不在单个节点上使用 Docker Swarm(而不是 Docker-Compose)?

    有 Docker Swarm 现已内置于 Docker 中 和 Docker Compose 人们似乎只在单个节点上运行容器时使用 Docker Compose 但是 Docker Compose 不支持任何deploy配置值 请参见htt
  • jqGrid:禁用排序

    我使用手动将行添加到 jqGridaddRowData 并且必须保持这些行的顺序 但是 在分页时 所有行都会重新排序以按网格的第一个可见列进行排序 我已经关闭了所有可以找到记录的排序选项 如何防止所有排序并维护所有页面中的行顺序 Code
  • 如何使用 LINQ 将这些数据组织成我想要的对象?

    我有一个以下类 我试图创建一个包含我查询过的一堆数据的列表 public class Agency public string AgencyName get set public int AgencyID get set public IE
  • Weblogic 12.1.3 PrivilegedActions 类未找到

    我创建了一个简单的项目 调用 jms 队列并放入消息 这里是代码 public class QueueSend Defines the JNDI context factory public final static String JNDI
  • 在 iOS 配置门户上创建 AdHoc 配置文件时出错 [已关闭]

    这个问题不太可能对任何未来的访客有帮助 它只与一个较小的地理区域 一个特定的时间点或一个非常狭窄的情况相关 通常不适用于全世界的互联网受众 为了帮助使这个问题更广泛地适用 访问帮助中心 help reopen questions 每当我尝试
  • IE想要从Django下载返回的JSON

    我有一个 Django 站点 其中一个页面正在执行基于 AJAX 的文件上传 使用Valum 的文件上传器 http valums com ajax upload 通过 JSON 返回一些信息 Django 返回 JSON 的方式是 ret
  • Dapper for NET Core:插入表并返回插入行的 id [重复]

    这个问题在这里已经有答案了 我的存储库中有以下方法 到目前为止 我相信返回的 int 只是指示操作是否成功的一个 我希望 int 是成功执行后返回的 id 表的单列 我该如何做到这一点 public async Task
  • 邮件中的证书链错误中的自签名证书

    我尝试编写一个简单的邮件程序 我用了节点邮件程序 and SMTP协议模块 我执行了 但它显示一个错误 例如 邮件中的证书链错误中的自签名证书 问题是什么 var express require express var app expres
  • 在 vscode 中调试 Serverless 时未命中断点

    在 VSCode 中调试基于无服务器的应用程序时 我的断点均未处于活动状态 启动 json configurations console integratedTerminal cwd workspaceRoot name Debug por
  • 如何正确使用 LINQ 和 MySQL? [复制]

    这个问题在这里已经有答案了 可能的重复 LINQ to MySQL 最好的选择是什么 https stackoverflow com questions 1469100 linq to mysql what is the best opti
  • Virtualenv 与 Eclipse (Galileo) [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 有人可以指导让 Eclipse Galileo PyDev 和 Virtualenv 一起工作吗 我专门尝试运行 Pinax 但任何说
  • C++:处理线程本地对象销毁

    我有一个日志系统 它基本上使用线程本地缓冲区来记录 这有助于减少锁定 可以将一堆消息写入线程本地缓冲区并一次性刷新 而且由于它是线程本地的 我们可以避免为每个日志消息分配缓冲区 无论如何 问题是在进程退出期间 我们在访问线程本地缓冲区时看到