我正在阅读Anthony Williams的“行动中的C ++并发”和第5章,其中讨论了新的多线程感知内存模型和原子操作,并指出:
为了对某些用户定义的
std::atomic<UDT>
使用UDT
,此类型必须具有普通复制赋值运算符。
据我了解,这意味着如果以下内容返回true,我们可以使用std::atomic<UDT>
:
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;
}
这是一个未定义行为的情况,恰好按预期行事吗?
提前致谢!
答案 0 :(得分:41)
标准未指定std::atomic<std::string>
的专精,因此适用通用template <typename T> std::atomic<T>
。 29.5 [atomics.types.generic] p1陈述:
有一个泛型类模板atomic。模板参数T的类型应该是可以轻易复制的(3.9)。
没有声明该实施必须诊断违反此要求的行为。因此,(a)您使用std::atomic<std::string>
调用未定义的行为,或(b)您的实现提供std::atomic<std::string>
作为符合标准的扩展。
查看std::atomic<T>
(http://msdn.microsoft.com/en-us/library/vstudio/hh874651.aspx)的MSDN页面,它确实明确提到T
可以轻易复制的要求,并且没有说明std::atomic<std::string>
的具体内容}。如果它是扩展名,则没有记录。我的钱是未定义的行为。
具体来说,17.6.4.8/1适用(with thanks to Daniel Krügler for setting me straight):
在某些情况下(替换函数,处理函数,用于实例化标准库模板组件的类型的操作),C ++标准库依赖于C ++程序提供的组件。如果这些组件不符合要求,则标准对实施没有要求。
std::string
当然不符合模板参数std::atomic<T>
可以轻易复制的T
要求,因此标准对实现没有要求。作为实施质量问题,请注意static_assert(std::is_trivially_copyable<T>::value, "std::atomic<T> requires T to be trivially copyable");
是一种易于诊断以捕获此违规行为的问题。
2016-04-19更新:我不知道更改何时发生,但VS2015 Update 2现在确诊std::atomic<std::string>
:
error C2338: atomic requires T to be trivially copyable.
答案 1 :(得分:6)
不,这是未定义的行为。此外,由于std :: string不是易于复制的,因此符合标准的编译器应该发出“至少一条诊断消息”:
29.5原子类型
有一个泛型类模板atomic。模板参数T的类型应该是可以轻易复制的(3.9)。
1.4实施合规性
- 如果某个程序包含违反任何可诊断规则[...]符合规定的内容 实施应至少发出一条诊断信息。
答案 2 :(得分:0)
为什么你认为这会正常运作&#39;当有多个线程试图读/写std::atomic<std::string>
?
这是C ++,你绝对可以用脚射击自己。如果您想使用不满足您可以使用的要求的类型,编译器可以&#34;可能&#34; (不会!)阻止你,但是当多个线程尝试读/写字符串时,你会在某个时刻开始看到奇怪/无法解释的行为。
此要求用于保证读取和写入的原子性,如果对象不是可轻易复制的,则可视化此场景:字符串具有旧值&#34;在里面。 1 Writer问题.store(&#34; New Data&#34;),现在有另一个线程在同一个变量上发出.load(),现在没有trivially_copyable属性,读者线程可以看到&#34; Nld Value& #34;或者&#34;新价值&#34;它不能原子地更新,因此结果很奇怪。
由于您发布的示例是顺序代码,因此不会发生这种情况。