简体   繁体   English

并发读取非原子变量

[英]Concurrent reads on non-atomic variable

I encounter this question while trying to implement a shared pointer. 我在尝试实现共享指针时遇到了这个问题。 Let's focus on the managed data pointer. 让我们关注托管数据指针。 Its lifetime can be divided into three stages: 它的生命周期可以分为三个阶段:

  1. Construction where there is no concurrent access. 没有并发访问的构造。
  2. Concurrent reads on it (no writes). 并发读取(无写入)。
  3. Destruction where there is no concurrent access. 没有并发访问的破坏。 This is guaranteed by reference counting. 这是通过引用计数来保证的。

My question is, given this situation, is it necessary for the pointer to be atomic? 我的问题是,鉴于这种情况,指针是否必须是原子的? I think it's equivalent to: will stage 2 lead to undefined behavior if the pointer is not atomic? 我认为这等效于:如果指针不是原子的,第二阶段会导致未定义的行为吗? Ideally, I want to hear an answer discussing from both a theoretical (language-lawyer) point of view and a practical point of view. 理想情况下,我希望听到从理论(语言律师)角度和实践角度进行讨论的答案。 For example, if not atomic, stage 2 may be undefined behavior theoretically, but is practically OK on actual platforms. 例如,如果不是原子的,则阶段2在理论上可能是未定义的行为,但在实际平台上实际上是可以的。 For implementing shared pointer, if non-atomic is OK, the managed pointer can be unique_ptr<T> , otherwise it has to be atomic<T*> . 为了实现共享指针,如果non-atomic可以,托管的指针可以是unique_ptr<T> ,否则必须是atomic<T*>

Update 更新

I find the standard text (Section 1.10 p21): 我找到了标准文字(第1.10页p21):

The execution of a program contains a data race if it contains two conflicting actions in different threads, at least one of which is not atomic, and neither happens before the other. 如果程序的执行在不同线程中包含两个冲突的动作,则其中至少有一个不是原子的,并且在两个线程之前都没有发生,因此该程序的执行将引起数据竞争。 Any such data race results in undefined behavior. 任何此类数据争用都会导致未定义的行为。

I guess concurrent reads do not classify as conflicting actions. 我猜并发读取不会归类为冲突操作。 Could somebody find some standard text about this to be sure? 可以肯定有人找到一些标准的文字吗?

The rule is that if more than one thread accesses the same object at the same time and at least one of those threads is modifying the data, then you have a data race, and the behavior of the program is undefined. 规则是,如果一个以上的线程同时访问同一对象,并且其中至少有一个线程正在修改数据,则您将发生数据争用,并且程序的行为是不确定的。 If nobody is modifying the object there is no problem from the concurrent accesses. 如果没有人在修改对象,则并发访问不会有问题。

Find the answer myself. 自己找到答案。 Quoted from first paragraph, Section 5.1.2 in C++ Concurrency in Action : 引用自第一段, C ++并发性中的 5.1.2节:

[...] If neither thread is updating the memory location, you're fine; [...]如果两个线程都没有更新内存位置,就可以了; read-only data doesn't need protection or synchronization. 只读数据不需要保护或同步。 If either thread is modifying the data, there's a potential for a race condition, as described in chapter 3. 如果任何一个线程正在修改数据,则存在争用条件的可能性,如第3章所述。

Concurrent reads on any variable, whether atomic or not, do not constitute a data race, because of the definition of conflicting evaluations, found in [intro.multithread] : 由于[intro.multithread]中存在冲突的评估定义,因此并发读取任何变量(无论是否为原子)都不构成数据争用:

Two expression evaluations conflict if one of them modifies a memory location and the other one accesses or modifies the same memory location. 如果两个表达式评估之一修改一个内存位置,而另一个表达式访问或修改相同的内存位置,则这两个表达式评估会发生冲突

Recently, this has moved to [intro.races] with a very subtle change in wording 最近,此词已移至[intro.races] ,措辞非常微妙

Two expression evaluations conflict if one of them modifies a memory location and the other one reads or modifies the same memory location. 如果两个表达式求值之一修改一个内存位置,而另一个表达式读取或修改了相同的内存位置,则这两个表达式求值会冲突

The change from accesses to reads took place between draft n4296 and n4431. 访问读取的更改发生在n4296和n4431草案之间。 The splitting of the multithreading section took place between n4582 and n4604. 多线程部分的拆分在n4582和n4604之间进行。

So I suppose you are talking of the structure holding the counter and a pointer to the owned data: 因此,我想您正在谈论持有计数器和指向所拥有数据的指针的结构:

template<class ValueType>
struct shared_counter{
  std::atomic<int> count=0;
  const std::unique_ptr<ValueType> ptr;
  //Your question is: Does ptr should be atomic?
  //... This is a dumb implementation, only focusing on the subject.
  };

In practice, ptr does not need to be atomic because if reference counting is implemented appropriatly, all access to ptr will be sequenced before the desctruction of shared_counter. 实际上,ptr不必是原子的,因为如果适当地实现了引用计数,则对ptr所有访问都将在对shared_counter进行说明之前进行排序。

To ensure that, inside the destructor of a shared_ptr the counter is decremented through a read-modify-write with acquire-release memory order: 为确保在shared_ptr的析构函数内,计数器通过具有获取释放内存顺序的读-修改-写操作递减:

template<class ValueType>
struct shared_ptr{
   shared_counter<ValueType>* counted_ptr;
   //...
   void reset(){
     if (counted_ptr->count.fetch_sub(1,std::memory_order_acq_rel) == 1)
       counter_ptr->~shared_counter<ValueType>();
     counter_ptr=nullptr;
     }
   };

Thanks to this memory order, if the fetched value of count is 1 in thread A, this means that in all other threads where other shared_ptrs pointing to the same shared_counter will not access any more this shared_counter . 由于此内存顺序,如果在线程A中获取的count值为1,这意味着在所有其他线程中,指向相同shared_counter 其他 shared_ptrs将不再访问此shared_counter The memory order ensure that accesses performed to this shared_counter in these other threads will happen before the fetch of the value 1 in thread A. (release in other thread -> acquire in the thread that will call the destructor). 内存顺序确保了在其他线程中对该共享计数器执行的访问将在获取线程A中的值1之前发生(在其他线程中释放->在将调用析构函数的线程中获取)。

So there is no need to have ptr to be atomic because the counter decrementation cause sufficient sequencing. 因此,无需将ptr设为原子,因为计数器的递减会导致足够的排序。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 原子地读取非原子变量? - Read a non-atomic variable, atomically? 并发非原子读/写是未定义的行为吗? - Is Concurrent Non-Atomic Read/Write An Undefined Behavior? OpenMP原子和非原子读/写在x86_64上产生相同的指令 - OpenMP atomic and non-atomic reads/writes produce the same instructions on x86_64 与原子变量相同的高速缓存行的非原子加载会导致原子变量失败吗? - Will a non-atomic load to the same cache line as an atomic variable cause the atomic variable to fail? 通过联合非原子访问原子 - Non-atomic access to atomic through a union 是否有 function 以原子方式加载非原子值? - Is there a function to load a non-atomic value atomically? 如何在不阻塞的情况下保护非原子变量不被中断和应用程序代码修改 - How to protect a non-atomic variable from modification by both interrupt and application code without blocking 在不锁定的情况下在不同线程中读取(仅)相同的非原子变量是否安全? - Is it safe to read(only) the same non-atomic variable in different threads without locking? std :: memory_order_acq_rel对在其他线程中读取的非原子变量的影响 - Effect of std::memory_order_acq_rel on non-atomic variable read in other thread 如何在 C++ 中混合原子和非原子操作? - How to mix atomic and non-atomic operations in C++?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM