【问题标题】:std::call_once lazy initialization issue on QNXQNX 上的 std::call_once 延迟初始化问题
【发布时间】:2016-03-22 14:53:04
【问题描述】:

我的代码在 QNX 上定期崩溃。它因错误而崩溃

错误读取变量:无法访问地址0x85dd6ac的内存)

在尝试访问 0x85dd6ac 对象的 std::map 成员变量时,该对象是使用 std::call_once 进行延迟初始化的。

使用以下伪代码完成初始化:

mutable std::aligned_storage<sizeof(A), alignof(A) >::type m_value;

void init(A *ptr)
{
    new (ptr) A();
}

inline T* data() const
{
    return reinterpret_cast<A*>(&m_value);
}

const A& get() const
{
    std::call_once(m_once_flag, init, data());
    return *data();
}

在访问get() 返回的对象时,进程会崩溃。

在其他平台上问题无法重现,调试非常困难。 从代码中我可以看到该对象不能被取消初始化,也不能被删除。

我怀疑std::call_once 实现可能存在线程安全或内存排序问题。 有人在 QNX 平台上使用 std::call_once 或类似的错误吗? 有什么想法可以找到问题吗?

【问题讨论】:

  • 你在使用 GCC 吗?大胆猜测:QNX Pthreads 实现仅支持将 PTHREAD_ONCE_INIT 用于静态对象,而 std::once_flag 类型中的数据成员的使用不起作用。如果您使用全局 std::once_flag 而不是 m_once_flag,会有所不同吗? (我意识到这意味着您只能执行一次初始化,而不是每个对象一次,但它可能有助于追踪问题)。
  • @Jonathan,它是mutable,所以它必须是一个非静态数据成员(“m_”前缀也强烈建议!)
  • 为什么不将变量仅在需要存储时才存储在地图中?没看懂
  • 我在这里可能已经过时了,但我上次检查 QNX 支持的 GCC 是 4.7 版。 C++11 支持有点不稳定。
  • 使用低级 POSIX pthread_once 能解决问题吗?

标签: c++ multithreading c++11 lazy-initialization qnx


【解决方案1】:

我在 QNX 中使用 std::call_once 有相同的体验,但除了崩溃之外,它还可能导致多线程应用程序中的死锁。我建议使用以下模式替换 std::call_once:

static std::atomic< bool > once_flag = false;
if ( !once_flag.exchange( true ) )
{
    // This part will be executed only once.
    // ...
}

更新 根据 fefe 的评论,该解决方案不满足阻塞被动执行的条件(详见http://www.cplusplus.com/reference/mutex/call_once/)。

如果您也必须满足这一点,您应该实施更复杂的解决方案。这是一个例子:

static std::atomic< bool > once_flag = false;
static std::atomic< bool > once_call_done = false;
if ( !once_flag.exchange( true ) )
{
    // This part will be executed only once.
    // ...

    once_call_done = true;
}
else
{
    // Block until the call once part is running.
    while( !once_call_done )
    {
        sleep( 1 );
    }
}

【讨论】:

  • 问题是,这个块后面的代码可能会在初始化执行完成之前执行。由于标志在初始化完成之前被更改,其他线程可能会在初始化完成之前发现one_flagtrue
  • 谢谢,好点子。我会根据你的评论修改我的答案。
【解决方案2】:

问题在于 std::call_once。这是实现中的错误。 用互斥锁临时替换解决了这个问题。 没有更多时间深入研究细节,但希望这些信息对遇到类似问题的人有所帮助。

感谢大家的cmets!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-10
    • 1970-01-01
    • 1970-01-01
    • 2011-11-17
    相关资源
    最近更新 更多