宽松的原子计数器安全吗?
Is a relaxed atomic counter safe?
根据 C++11 内存模型,以下代码是否保证 return 计数器的预期值 (40,000,000)? (不 限于 x86)。
#include <atomic>
#include <thread>
using namespace std;
void ThreadProc(atomic<int>& counter)
{
for (int i = 0; i < 10000000; i++)
counter.fetch_add(1, memory_order_relaxed);
}
int main()
{
#define COUNT 4
atomic<int> counter = { 0 };
thread threads[COUNT] = {};
for (size_t i = 0; i < COUNT; i++)
threads[i] = thread(ThreadProc, ref(counter));
for (size_t i = 0; i < COUNT; i++)
threads[i].join();
printf("Counter: %i", counter.load(memory_order_relaxed));
return 0;
}
特别是,将放宽原子坐标,使两个线程不会同时读取当前值,独立地递增它,并且都写入它们递增的值,有效地失去之一写?
规范中的某些行似乎表明在上面的示例中,计数器 必须 始终为 40,000,000。
[Note: operations specifying memory_order_relaxed are relaxed with
respect to memory ordering. Implementations must still guarantee that
any given atomic access to a particular atomic object be indivisible
with respect to all other atomic accesses to that object. — end note
.
Atomic read-modify-write operations shall always read the last value
(in the modification order) written the write associated with the
read-modify-write operation.
.
All modifications to a particular atomic object M occur in some
particular total order, called the modification order of M. If A and B
are modifications of an atomic object M and A happens before (as
defined below) B, then A shall precede B in the modification order of
M, which is defined below.
这个演讲也支持上述代码没有竞争的观点。
https://www.youtube.com/watch?v=KeLBd2EJLOU&feature=youtu.be&t=1h9m30s
在我看来,是原子操作的不可分割的顺序,但我们无法保证顺序是什么。因此,所有增量都必须在没有我上面描述的竞争的情况下发生 'one before the other'。
但是有一些事情可能指向另一个方向:
Implementations should make atomic stores visible to atomic loads
within a reasonable amount of time.
一位同事告诉我,Sutter 的演讲中存在已知错误。虽然我还没有找到任何来源。
C++ 社区的多个成员比我暗示的更聪明,可以缓冲宽松的原子添加,以便后续的宽松原子添加可以读取和操作陈旧值。
您问题中的代码是免费的;所有的增量都是有序的,40000000的结果是有保证的。
您问题中的参考文献包含标准中的所有相关引述。
其中所说的原子存储应在合理时间内可见的部分仅适用于单个存储。
在您的情况下,计数器通过原子读取-修改-写入操作递增,并且保证在修改顺序中的最新操作。
Multiple members of the C++ community (...) have implied that a relaxed atomic add could be buffered such that a subsequent relaxed atomic add could read and operator on the stale value.
这是不可能的,只要修改是基于原子读-修改-写操作。
如果标准不能保证可靠的结果,原子增量将毫无用处
根据 C++11 内存模型,以下代码是否保证 return 计数器的预期值 (40,000,000)? (不 限于 x86)。
#include <atomic>
#include <thread>
using namespace std;
void ThreadProc(atomic<int>& counter)
{
for (int i = 0; i < 10000000; i++)
counter.fetch_add(1, memory_order_relaxed);
}
int main()
{
#define COUNT 4
atomic<int> counter = { 0 };
thread threads[COUNT] = {};
for (size_t i = 0; i < COUNT; i++)
threads[i] = thread(ThreadProc, ref(counter));
for (size_t i = 0; i < COUNT; i++)
threads[i].join();
printf("Counter: %i", counter.load(memory_order_relaxed));
return 0;
}
特别是,将放宽原子坐标,使两个线程不会同时读取当前值,独立地递增它,并且都写入它们递增的值,有效地失去之一写?
规范中的某些行似乎表明在上面的示例中,计数器 必须 始终为 40,000,000。
[Note: operations specifying memory_order_relaxed are relaxed with respect to memory ordering. Implementations must still guarantee that any given atomic access to a particular atomic object be indivisible with respect to all other atomic accesses to that object. — end note
.
Atomic read-modify-write operations shall always read the last value (in the modification order) written the write associated with the read-modify-write operation.
.
All modifications to a particular atomic object M occur in some particular total order, called the modification order of M. If A and B are modifications of an atomic object M and A happens before (as defined below) B, then A shall precede B in the modification order of M, which is defined below.
这个演讲也支持上述代码没有竞争的观点。 https://www.youtube.com/watch?v=KeLBd2EJLOU&feature=youtu.be&t=1h9m30s
在我看来,是原子操作的不可分割的顺序,但我们无法保证顺序是什么。因此,所有增量都必须在没有我上面描述的竞争的情况下发生 'one before the other'。
但是有一些事情可能指向另一个方向:
Implementations should make atomic stores visible to atomic loads within a reasonable amount of time.
一位同事告诉我,Sutter 的演讲中存在已知错误。虽然我还没有找到任何来源。
C++ 社区的多个成员比我暗示的更聪明,可以缓冲宽松的原子添加,以便后续的宽松原子添加可以读取和操作陈旧值。
您问题中的代码是免费的;所有的增量都是有序的,40000000的结果是有保证的。
您问题中的参考文献包含标准中的所有相关引述。
其中所说的原子存储应在合理时间内可见的部分仅适用于单个存储。
在您的情况下,计数器通过原子读取-修改-写入操作递增,并且保证在修改顺序中的最新操作。
Multiple members of the C++ community (...) have implied that a relaxed atomic add could be buffered such that a subsequent relaxed atomic add could read and operator on the stale value.
这是不可能的,只要修改是基于原子读-修改-写操作。
如果标准不能保证可靠的结果,原子增量将毫无用处