std::atomic<std::string> 是不是正常工作?

Posted

技术标签:

【中文标题】std::atomic<std::string> 是不是正常工作?【英文标题】:Does std::atomic<std::string> work appropriately?std::atomic<std::string> 是否正常工作? 【发布时间】:2013-05-28 09:41:39 【问题描述】:

我正在阅读 Anthony Williams 的“C++ Concurrency in Action”和第 5 章,其中讨论了新的多线程感知内存模型和原子操作,他说:

为了将std::atomic&lt;UDT&gt; 用于某些用户定义的UDT,此类型必须具有普通 复制赋值运算符。

据我了解,这意味着如果以下返回 true,我们可以使用std::atomic&lt;UDT&gt;

std::is_trivially_copyable<UDT>::value

按照这种逻辑,我们不应该使用std::string 作为std::atomic 的模板参数并让它正常工作。

但是,以下代码编译并运行时会产生预期的输出:

#include <atomic>
#include <thread>
#include <iostream>
#include <string>

int main()

    std::atomic<std::string> atomicString;

    atomicString.store( "TestString1" );

    std::cout << atomicString.load() << std::endl;

    atomicString.store( "TestString2" );

    std::cout << atomicString.load() << std::endl;

    return 0;

这是一种未定义的行为,只是碰巧按预期运行吗?

提前致谢!

【问题讨论】:

你的编译器是什么(以及你的标准库的实现)?我不能让它编译器here,实际上这就是我所期待的 当您使用它时,我预计不会出现问题。当两个(或更多)线程试图同时修改同一个字符串时,就会出现问题。那时,string 的非平凡操作符将开始引起问题。只是在std::atomic 中包装一些东西不太可能破坏没有它就可以的代码。同时,如果不遵守它的规则,它也无助于那些没有它就会被破坏的代码。 这几乎肯定是“偶然的”未定义行为:Microsoft 的字符串实现使用了小字符串优化,您正在测试的小字符串可以有效地轻松复制。如果您使用更长的字符串 - 以便启动堆分配 - 您应该会看到更加丰富多彩的效果。 你当然不会失败,你只有一个线程!如果您有多个线程访问字符串,则会发生 UB,因此修改可能不像您希望的那样“原子”。 (UB!) 一个普通的std::string 至少有 3 个指针大小的成员,因此在任何主流 C++ 实现中它永远不会是 lock_free。让 std::atomic 对每个访问进行锁定而不是仅仅使用你自己的互斥锁基本上是没有意义的。 【参考方案1】:

标准没有指定std::atomic&lt;std::string&gt; 的特化,所以通用的template &lt;typename T&gt; std::atomic&lt;T&gt; 适用。 29.5 [atomics.types.generic] p1 状态:

有一个通用类模板原子。模板参数 T 的类型应该是可简单复制的 (3.9)。

没有声明说实现必须诊断违反此要求的情况。因此,要么 (a) 您使用 std::atomic&lt;std::string&gt; 调用未定义的行为,要么 (b) 您的实现提供 std::atomic&lt;std::string&gt; 作为符合标准的扩展。

查看 std::atomic&lt;T&gt; (http://msdn.microsoft.com/en-us/library/vstudio/hh874651.aspx) 的 MSDN 页面,它确实明确提到了 T 可以轻松复制的要求,并且没有具体说明 std::atomic&lt;std::string&gt;。如果它是一个扩展,它是无证的。我的钱花在了未定义的行为上。

具体来说,17.6.4.8/1 适用 (with thanks to Daniel Krügler for setting me straight):

在某些情况下(替换函数、处理函数、对用于实例化标准库模板组件的类型的操作),C++ 标准库依赖于 C++ 程序提供的组件。如果这些组件不满足其要求,则标准不会对实施提出任何要求。

std::string 肯定不满足std::atomic&lt;T&gt; 模板参数T 可以简单复制的要求,因此标准对实现没有要求。作为一个实施质量问题,请注意static_assert(std::is_trivially_copyable&lt;T&gt;::value, "std::atomic&lt;T&gt; requires T to be trivially copyable"); 是一种容易发现此违规行为的诊断方法。


2016-04-19 更新:我不知道变化是什么时候发生的,但是 VS2015 Update 2 现在确实诊断出std::atomic&lt;std::string&gt;

错误 C2338: atomic 要求 T 可以轻松复制。

【讨论】:

gcc 也捕捉到了这一点(使用 gcc 8.2.0):error: static assertion failed: std::atomic requires a trivially copyable type.【参考方案2】:

不,这是未定义的行为。此外,由于 std::string 不可轻易复制,因此符合标准的编译器应该发出“至少一条诊断消息”:

29.5 原子类型

有一个通用类模板原子。模板参数 T 的类型应该是可简单复制的 (3.9)。

1.4 实施合规性

— 如果程序包含违反任何可诊断规则 [...] 实施应发出至少一条诊断消息。

【讨论】:

以上是关于std::atomic<std::string> 是不是正常工作?的主要内容,如果未能解决你的问题,请参考以下文章

原子读取然后用 std::atomic 写入

std::atomic_flag 停止多个线程

C++ error: use of deleted function ‘std::atomic<short unsigned int>::atomic(const std::atomic<short

在 Windows 中等待 std::atomic<int> 的正确方法?

未解析的外部符号“std::atomic_fetch_add”

ARM 上的 std::atomic<bool> 无锁不一致(树莓派 3)