为什么std :: atomic中的所有成员函数都带有和不带有volatile出现? [英] Why do all the member functions in std::atomic appear both with and without volatile?

查看:275
本文介绍了为什么std :: atomic中的所有成员函数都带有和不带有volatile出现?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我注意到 std :: atomic< T> 类型的大多数成员函数被声明两次,一次是 volatile 修饰符,并且一次不包含(示例))。我检查了G ++标准库实现的源代码,发现它们都是完全重复的,例如,

I noticed that most member functions of std::atomic<T> types are declared twice, once with the volatile modifier and once without (example)). I checked the source code of the G++ standard library implementation and I found all of them to be exact duplicates, for example,

bool
load(memory_order __m = memory_order_seq_cst) const noexcept
{ return _M_base.load(__m); }

bool
load(memory_order __m = memory_order_seq_cst) const volatile noexcept
{ return _M_base.load(__m); }

我找不到任何示例,其中 volatile 变体的行为与非 volatile 的行为不同,返回类型或类似内容有所不同。

I could not find any example where the volatile variant behaves differently than the non-volatile one, differs in return type or anything of that sort.

这是为什么?我认为 volatile 成员函数也可以在不是 volatile 的对象中调用。因此,声明并定义 std :: atomic :: load(...)const volatile noexcept 等就足够了。

Why is that? I thought a volatile member function could also be called in objects which are not volatile. So declaring and defining std::atomic::load(...) const volatile noexcept etc. should be enough.

更新:

根据评论,我的问题基本上可以归结为:您能否提供一个使用 non-< volatile 实例(不一定是 std :: atomic )的实例将在在以下两种情况下,

Based on the comments my question basically boils down to: Can you provide an example where some calls using a non-volatile instance (not necessarily of std::atomic) would generate a different assembly in the two following cases,


  1. 每个成员函数在具有和没有 volatile的情况下都具有相同的主体

仅存在 volatile 变体?

这是假定编译器可以执行标准允许的任何优化(或只是最高优化级别)。

This, assuming the compiler can do whatever optimization the standard allows it to (or simply highest optimization levels).

推荐答案

可能全部源于 volatile 是什么,为此,请参见此答案。与常规应用程序开发相比,用例非常狭小,这就是为什么通常没有人关心的原因。我将假设您没有任何实际的场景,您想知道是否应该应用那些易失性重载。然后,我将尝试举一个示例,您可能需要这些示例(不要认为它过于真实)。

Probably it all stems from what volatile is, for that see this answer. As the use-cases are quite slim compare to usual application development, that is why nobody usually cares. I will assume that you do not have any practical scenario where you wondering if you should apply those volatile overloadings. Then I will try to come up with an example where you may need those (do not judge it to be too real).

volatile std::sig_atomic_t status = ~SIGINT;
std::atomic<int> shareable(100);

void signal_handler(int signal)
{
  status = signal;
}

// thread 1
  auto old = std::signal(SIGINT, signal_handler);
  std::raise(SIGINT);
  int s = status;
  shareable.store(10, std::memory_order_relaxed);
  std::signal(SIGINT, old);

// thread 2
  int i = shareable.load(std::memory_order_relaxed);

memory_order_relaxed 保证原子性和修改顺序的一致性,没有副作用。 volatile 不能带有副作用进行重新排序。然后在这里,在线程2中,您可以获得 shared 等于10,但状态仍然不是 SIGINT 。但是,如果将类型限定符设置为 shared volatile ,则必须保证。为此,您将需要成员方法是 volatile 限定的。

memory_order_relaxed guarantees atomicity and modification order consistency, no side-effects. volatile cannot be reordered with side-effects. Then here we are, in thread 2 you can get shareable equal 10, but status is still not SIGINT. However, if you set type qualifier to volatile of shareable that must be guaranteed. For that you will need the member methods be volatile-qualified.

为什么要这样做呢? ?我可能会想到的一种情况是,您有一些旧代码正在使用基于 volatile 的旧内容,并且由于一个或另一个原因而无法对其进行修改。很难想象,但是我想可能需要在 atomic volatile 之间建立某种有保证的顺序内联汇编。底线,恕我直言,是只要有可能,您就可以使用新的原子库而不是 volatile 对象,如果存在某些 volatile 对象,它们是您无法摆脱的,并且想要使用 atomic 对象,那么您可能需要 volatile atomic 对象的$ c>限定符具有适当的顺序保证,因为这将需要重载。

Why would you do something like this at all? One case, I might think of, is you have some old code that is using old volatile-based stuff and you cannot modify it for one or another reason. Hard to imagine, but I guess that there might be a need to have some kind of guaranteed order between atomic and volatile inline assembly. The bottom line, IMHO, is that whenever it is possible you can use new atomic library instead of volatile objects, in the case there are some volatile objects, that you cannot rid of, and you want to use atomic objects, then you might need volatile qualifier for the atomic objects to have proper ordering guarantees, for that you would need overloading.

更新


但是,如果我只想让原子类型既可用于易失性又可用于非易失性,为什么不只是执行前者?

But if all I wanted was to have atomic types usable as both volatile and non-volatile, why not just implement the former?



struct Foo {
    int k;
};

template <typename T>
struct Atomic {
  void store(T desired) volatile { t = desired; }
  T t;
};

int main(int i, char** argv) {
    //error: no viable overloaded '='
    // void store(T desired) volatile { t = desired; }
  Atomic<Foo>().store(Foo());
  return 0;
}

load 和其他操作,因为这些操作通常不是琐碎的实现,需要复制操作符和/或复制构造函数(也可以 volatile 或非易失性)。

The same would be with load and other operations, because those are usually not trivial implementations that require copy operator and/or copy constructor (that also can volatile or non-volatile).

这篇关于为什么std :: atomic中的所有成员函数都带有和不带有volatile出现?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆