【问题标题】:Is initializing a atomic pointer atomic? What happens if initialization or memory allocation throws?初始化原子指针是原子的吗?如果初始化或内存分配抛出,会发生什么?
【发布时间】:2018-05-14 09:57:55
【问题描述】:

如果我一次性声明并定义一个原子指针 -

std::atomic<int*> iptr = new int(1);    
std::atomic<T*> iptr = new T();

据我了解,整个操作不是原子的。

new T() 涉及分配内存,构造 T 对象,然后将其原子分配给 iptr。 T 可能很容易构造,在这种情况下构造 T 不应该抛出,但某些用户定义的 T 可能会抛出。

如果在 T 构造或内存分配之间有其他线程使用 iptr 怎么办?

这个操作真的是原子的吗?使其原子化的一种方法是破坏声明和定义,例如

T* temp = new T();
std::atomic<T*> iptr = temp;    

还有其他方法可以原子地做同样的事情吗? 我的理解有问题吗?

【问题讨论】:

  • 请记住,虽然T* 可能是原子的;如果您有 2 个线程尝试同时分配它,您最终会出现内存泄漏。这是因为第一个将分配它 - 并成功;然后第二个将分配它,而不是从第一个释放内存。您可能想要交换而不是分配,以便如果它有一个值,您可以释放它(如果另一个线程正在使用它,这可能会导致崩溃)。

标签: c++ c++11 c++14


【解决方案1】:

new T() 涉及分配内存,

是的。

构造T 对象

是的。

然后它将自动分配给 iptr。

完全正确(几乎是这里使用的对象的地址)。它用于在内存分配和构造完成后初始化原子。

T 可能很容易构造,在这种情况下构造 T 不应该抛出,但某些用户定义的 T 可能会抛出。

如果是这样,将无法完成原子的初始化。如果 c'tor 抛出,new 表达式不会泄漏内存,它会调用正确的 dealcoation 函数。因此,您的临时和直接使用新表达式具有完全相同的语义。

【讨论】:

    【解决方案2】:

    新的未命名对象的创建不是原子的,但也不是必须的。 [如上所述,任何异常只会影响在分配给原子之前创建的临时对象的生命周期,并且应该清理干净。]

    将该临时指针分配给您的原子指针是原子的。

    我最担心的是,您的代码无法检测到是否有另一个线程完成了分配,因此您必须考虑使用 testandset 或 swap。然后你就有了测试/交换失败的麻烦,并且你有一个指向你不再需要的对象的临时指针。

    如果您使用局部变量(而不是未命名的临时变量),您就有机会清理它。

    如果您对原子和临时指针使用智能指针,它将自毁。

    但是,在许多情况下,除非您确实需要临时对象,否则最好不要创建临时对象,但这意味着在整个创建过程中都有一个互斥体,但是您可以在进入互斥体之前进行轻量级检查。这更像是单例静态变量所做的。
    如果您正在创建单例,则没有真正的缺点,因为其他线程无论如何都必须等待。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-01-21
      • 2018-11-30
      • 1970-01-01
      • 2019-11-07
      • 1970-01-01
      • 2022-09-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多