std::原子工作合适吗?

时间:2023-01-06 21:25:39

I am reading through Anthony Williams' "C++ Concurrency in Action" and in Chapter 5, which talks about the new multithreading-aware memory model and atomic operations, and he states:

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

In order to use std::atomic<UDT> for some user-defined UDT, this type must have a trivial copy assignment operator.

为了对某些用户定义的UDT使用std::atomic ,该类型必须具有一个普通的复制分配操作符。

As I understand it, this means that we can use std::atomic<UDT> if the following returns true:

正如我所理解的,这意味着我们可以使用std::原子 ,如果以下返回的是true:

std::is_trivially_copyable<UDT>::value

By this logic, we shouldn't be able to use std::string as a template argument for std::atomic and have it work correctly.

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

However, the following code compiles and runs with expected output:

但是,下面的代码编译并运行预期的输出:

#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;
}

Is this a case of undefined behaviour which just happens to behave as expected?

这是一种没有定义的行为吗?

Thanks in advance!

提前谢谢!

3 个解决方案

#1


39  

The standard does not specify a specialization of std::atomic<std::string>, so the generic template <typename T> std::atomic<T> applies. 29.5 [atomics.types.generic] p1 states:

标准没有指定std::atomic(原子) 的专门化,因此通用模板 std: atomic(原子) 。29.5[atomics.types。通用)p1状态:

There is a generic class template atomic. The type of the template argument T shall be trivially copyable (3.9).

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

There is no statement that the implementation must diagnose violations of this requirement. So either (a) your use of std::atomic<std::string> invokes undefined behavior, or (b) your implementation provides std::atomic<std::string> as a conforming extension.

没有声明说实现必须诊断违反此需求的情况。因此,要么(a)使用std::原子 <:string> 调用未定义的行为,或者(b)您的实现提供std::原子 <:string> 作为一个一致性扩展。

Looking at the MSDN page for std::atomic<T> (http://msdn.microsoft.com/en-us/library/vstudio/hh874651.aspx), it does explicitly mention the requirement that T be trivially copyable, and it does NOT say anything specific about std::atomic<std::string>. If it is an extension, it's undocumented. My money is on undefined behavior.

查看std::atomic (http://msdn.microsoft.com/en-us/library/vstudio/hh874651.aspx)的MSDN页面,它确实明确地提到了T是可受版权保护的要求,并且没有具体说明std: atomic <:::string> 。如果是扩展名,就没有文档。我的钱花在不明确的行为上。

Specifically, 17.6.4.8/1 applies (with thanks to Daniel Krügler for setting me straight):

具体来说,17.6.4.8/1适用(感谢Daniel Krugler为我澄清):

In certain cases (replacement functions, handler functions, operations on types used to instantiate standard library template components), the C++ standard library depends on components supplied by a C++ program. If these components do not meet their requirements, the Standard places no requirements on the implementation.

在某些情况下(替换函数、处理程序函数、用于实例化标准库模板组件的类型的操作),c++标准库依赖于c++程序提供的组件。如果这些组件不满足它们的需求,则该标准对实现没有要求。

std::string certainly does not meet the std::atomic<T> requirement that the template parameter T be trivially copyable, so the standard places no requirements on the implementation. As a quality of implementation issue, note that static_assert(std::is_trivially_copyable<T>::value, "std::atomic<T> requires T to be trivially copyable"); is an easy diagnostic to catch this violation.

string当然不满足std::atomic 要求模板参数T是可复制的,因此标准对实现没有要求。作为实现的质量问题,请注意static_assert(std::: is_triviallyally_copyable ::value,“std: atomic 要求T具有基本的可复制性”);是一种很容易发现这种违反的诊断。


2016-04-19 Update: I don't know when the change happened, but VS2015 Update 2 does now diagnose std::atomic<std::string>:

2016-04-19更新:我不知道更改是什么时候发生的,但是VS2015更新2现在确实诊断了std::atomic :

error C2338: atomic requires T to be trivially copyable.

#2


6  

No, this is undefined behavior. Moreover, since std::string is not trivially copyable, conforming compiler should have issued "at least one diagnostic message":

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

29.5 Atomic types

29.5原子类型

There is a generic class template atomic. The type of the template argument T shall be trivially copyable (3.9).

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

1.4 Implementation compliance

1.4实现遵从性

— If a program contains a violation of any diagnosable rule [...] a conforming implementation shall issue at least one diagnostic message.

-如程序违反任何可诊断规则[…符合要求的实现应至少发出一条诊断信息。

#3


0  

Why do you think this will work 'correctly' when there are multiple threads trying to read/write the std::atomic<std::string>?

为什么当有多个线程试图读/写std::atomic 时,您认为这将会“正确”地工作?

This is C++, you are definitely allowed to shoot yourself in the foot. If you want to use a type which does not satisfy the requirements you are free to use, the compiler "may" (not will!) stop you, but you will start seeing weird/unexplainable behaviour at some point when multiple threads try to read/write the string.

这是c++,你绝对可以在脚上开枪。如果您想使用不满足您可以*使用的需求的类型,编译器“may”(不是will!)会阻止您,但是当多个线程试图读取/写入字符串时,您将开始看到奇怪的/不可解释的行为。

This requirement is for guaranteeing atomicity of reads and writes, if the object is not trivially copyable then visualize this scene: The string had "Old Value" in it. 1 Writer issues .store("New Data"), now there is another thread which issues .load() on the same variable, now without a trivially_copyable property, the reader thread can see "Nld Value" or "New Value" etc. It can't be updated atomically, hence the weird results.

这一要求是为了保证读取和写入的原子性,如果对象不是可被trivially copyable,那么就可以可视化这个场景:该字符串在其中具有“旧值”。写入器发出。store(“New Data”),现在有另一个线程在同一个变量上发出。load(),现在没有一个trially_copyable属性,读取器线程可以看到“Nld值”或“New Value”等。

Since the example you posted is a sequential code, this does not happen.

因为您发布的示例是一个顺序代码,所以不会发生这种情况。

#1


39  

The standard does not specify a specialization of std::atomic<std::string>, so the generic template <typename T> std::atomic<T> applies. 29.5 [atomics.types.generic] p1 states:

标准没有指定std::atomic(原子) 的专门化,因此通用模板 std: atomic(原子) 。29.5[atomics.types。通用)p1状态:

There is a generic class template atomic. The type of the template argument T shall be trivially copyable (3.9).

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

There is no statement that the implementation must diagnose violations of this requirement. So either (a) your use of std::atomic<std::string> invokes undefined behavior, or (b) your implementation provides std::atomic<std::string> as a conforming extension.

没有声明说实现必须诊断违反此需求的情况。因此,要么(a)使用std::原子 <:string> 调用未定义的行为,或者(b)您的实现提供std::原子 <:string> 作为一个一致性扩展。

Looking at the MSDN page for std::atomic<T> (http://msdn.microsoft.com/en-us/library/vstudio/hh874651.aspx), it does explicitly mention the requirement that T be trivially copyable, and it does NOT say anything specific about std::atomic<std::string>. If it is an extension, it's undocumented. My money is on undefined behavior.

查看std::atomic (http://msdn.microsoft.com/en-us/library/vstudio/hh874651.aspx)的MSDN页面,它确实明确地提到了T是可受版权保护的要求,并且没有具体说明std: atomic <:::string> 。如果是扩展名,就没有文档。我的钱花在不明确的行为上。

Specifically, 17.6.4.8/1 applies (with thanks to Daniel Krügler for setting me straight):

具体来说,17.6.4.8/1适用(感谢Daniel Krugler为我澄清):

In certain cases (replacement functions, handler functions, operations on types used to instantiate standard library template components), the C++ standard library depends on components supplied by a C++ program. If these components do not meet their requirements, the Standard places no requirements on the implementation.

在某些情况下(替换函数、处理程序函数、用于实例化标准库模板组件的类型的操作),c++标准库依赖于c++程序提供的组件。如果这些组件不满足它们的需求,则该标准对实现没有要求。

std::string certainly does not meet the std::atomic<T> requirement that the template parameter T be trivially copyable, so the standard places no requirements on the implementation. As a quality of implementation issue, note that static_assert(std::is_trivially_copyable<T>::value, "std::atomic<T> requires T to be trivially copyable"); is an easy diagnostic to catch this violation.

string当然不满足std::atomic 要求模板参数T是可复制的,因此标准对实现没有要求。作为实现的质量问题,请注意static_assert(std::: is_triviallyally_copyable ::value,“std: atomic 要求T具有基本的可复制性”);是一种很容易发现这种违反的诊断。


2016-04-19 Update: I don't know when the change happened, but VS2015 Update 2 does now diagnose std::atomic<std::string>:

2016-04-19更新:我不知道更改是什么时候发生的,但是VS2015更新2现在确实诊断了std::atomic :

error C2338: atomic requires T to be trivially copyable.

#2


6  

No, this is undefined behavior. Moreover, since std::string is not trivially copyable, conforming compiler should have issued "at least one diagnostic message":

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

29.5 Atomic types

29.5原子类型

There is a generic class template atomic. The type of the template argument T shall be trivially copyable (3.9).

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

1.4 Implementation compliance

1.4实现遵从性

— If a program contains a violation of any diagnosable rule [...] a conforming implementation shall issue at least one diagnostic message.

-如程序违反任何可诊断规则[…符合要求的实现应至少发出一条诊断信息。

#3


0  

Why do you think this will work 'correctly' when there are multiple threads trying to read/write the std::atomic<std::string>?

为什么当有多个线程试图读/写std::atomic 时,您认为这将会“正确”地工作?

This is C++, you are definitely allowed to shoot yourself in the foot. If you want to use a type which does not satisfy the requirements you are free to use, the compiler "may" (not will!) stop you, but you will start seeing weird/unexplainable behaviour at some point when multiple threads try to read/write the string.

这是c++,你绝对可以在脚上开枪。如果您想使用不满足您可以*使用的需求的类型,编译器“may”(不是will!)会阻止您,但是当多个线程试图读取/写入字符串时,您将开始看到奇怪的/不可解释的行为。

This requirement is for guaranteeing atomicity of reads and writes, if the object is not trivially copyable then visualize this scene: The string had "Old Value" in it. 1 Writer issues .store("New Data"), now there is another thread which issues .load() on the same variable, now without a trivially_copyable property, the reader thread can see "Nld Value" or "New Value" etc. It can't be updated atomically, hence the weird results.

这一要求是为了保证读取和写入的原子性,如果对象不是可被trivially copyable,那么就可以可视化这个场景:该字符串在其中具有“旧值”。写入器发出。store(“New Data”),现在有另一个线程在同一个变量上发出。load(),现在没有一个trially_copyable属性,读取器线程可以看到“Nld值”或“New Value”等。

Since the example you posted is a sequential code, this does not happen.

因为您发布的示例是一个顺序代码,所以不会发生这种情况。