volatile 变量有用吗?如果是,那么什么时候? [英] Are volatile variables useful? If yes then when?

查看:62
本文介绍了volatile 变量有用吗?如果是,那么什么时候?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

回答这个问题让我想想还有什么对我来说还不清楚.首先假设我们阅读了这篇文章这篇文章.

Answering this question made me think about something is still not clear for me. Let's first assume we read all from this post and this post.

[开始编辑]也许不是那么明显(意大利幽默?!)但标题很挑衅:当然应该有一个理由,如果volatile 已包含在 C# 中,我只是无法理解确切的.[end edit]

[begin edit] Maybe it's not so obvious (Italian humor?!) but title is just pretty provocative: of course there should be a reason if volatile has been included in C#, I just can't understand exact one.[end edit]

简而言之,我们知道我们有三个工具可以在线程之间共享变量:

In short we know we have three tools to share a variable between threads:

  • lock 因为这将防止指令重新排序.
  • volatile 因为会强制 CPU 始终从内存中读取值(然后不同的 CPU/内核不会缓存它并且他们不会看到旧值).
  • 互锁操作(Increment/DecrementCompareExchange),因为它们将在单个原子中执行更改 + 赋值(fast 比例如 volatile + lock) 操作更快.
  • lock because this will prevent instruction reordering.
  • volatile because will force CPU to always read value from memory (then different CPUs/cores won't cache it and they won't see old values).
  • Interlocked operations (Increment/Decrement and CompareExchange) because they'll perform change + assignment in a single atomic (fast faster than, for example, a volatile + lock) operation.

我不明白的(C# 规范参考将不胜感激):

What I don't understand (C# specs reference would be appreciated):

  • 锁如何防止缓存问题?关键部分是否隐含了内存屏障?
  • 可变变量不能是局部的(我从 Eric Lippert 那里读到了一些关于这个的东西,但我现在找不到那个帖子,我不记得他的评论了 - 说实话 - 我什至不太明白出色地).这让我觉得它们不是用 Interlocked.CompareExchange() 和朋友们实现的,它们有什么不同?
  • How lock will prevent cache problem? Is it implicit a memory barrier in a critical section?
  • Volatile variables can't be local (I read something from Eric Lippert about this but I can't find that post now and I don't remember his comments and - to be honest - I didn't even understand it very well). This makes me think they're not implemented with an Interlocked.CompareExchange() and friends, in what they're different?

例如在这段代码中,volatile 修饰符会做什么?

What volatile modifier will do for example in this code?

volatile int _volatileField = 0;
int _normalField = 0;

void test()
{
    Interlocked.Increment(ref _normalField);
    ++_volatileField;
}

[begin edit] 前面的例子涉及到原子读+写,我们改成_volatileField = 1;,这里不谈原子操作.[结束编辑]

[begin edit] previous example involves atomic read + write, let's change it to _volatileField = 1;, here I'm not talking about atomic operations. [end edit]

此外,编译器(除了警告)会在这里做什么:

Moreover what compiler (beside warnings) will do here:

Interlocked.Increment(ref _volatileField);

它们看起来完全不同(就像我想象的那样),但就我的理解 Interlocked.Increment() 操作数应该隐式地是可变的(然后它只会添加原子增量).非易失性字段怎么可能?它们是否也意味着障碍?这不会对性能造成很大影响(与 volatile 相比)吗?

They seems pretty different things (as I would imagine they are) but for my understanding Interlocked.Increment() operand should implicitly be volatile (then it'll add just atomic increment). How is it possible for non volatile fields? Does they imply barriers too? Doesn't this hurt performance a lot (compared to volatile)?

<打击>如果 volatile 不暗示障碍但其他人这样做,那么为什么我们不能将它们用作局部变量?尤其是在例如并行循环中使用时,这将在很大程度上损害性能(我正在考虑使用少量代码处理大量数据的小函数,其中数据缓存可能会被很好地使用).

If volatile doesn't imply barriers but others do then why we can't use them as on local variables? Especially when used in, for example, parallel loops this will hurt performance in a significant way (I'm thinking about small functions with little code that work on a lot of data where data cache may be well used).

[begin edit] 我发现前一句真的不清楚(对不起我的英语).我的意思是:如果性能(volatileCompareExchange 相比,比较适用)更好(是的,我们可以衡量,并且在某些情况下差异是可衡量和可见的)那为什么我们不能将它们用于局部变量呢?我正在考虑处理大量数据的并行循环(其中开销和障碍可能会严重影响性能).[结束编辑]

[begin edit] I found previous sentence was really unclear (sorry for my English). What I mean is: if performance (of volatile compared to CompareExchange, where comparison is applicable) are better (yes we can measure and in some scenario difference is measurable and visible) then why we can't use them for local variables? I'm thinking about parallel loops that manipulates a lot of data (where both overhead and barriers may hurt performance a lot).[end edit]

推荐答案

这个问题很混乱.让我试着分解一下.

This question is very confusing. Let me try to break it down.

volatile 变量有用吗?

Are volatile variables useful?

是的.C# 团队不会添加无用的功能.

Yes. The C# team would not have added a useless feature.

如果是,那么什么时候?

If yes then when?

易失性变量在某些对性能高度敏感的多线程应用程序中很有用,其中应用程序架构基于跨线程共享内存.

Volatile variables are useful in certain highly performance-sensitive multithreaded applications where the application architecture is predicated on sharing memory across threads.

作为社论,我注意到正常的业务线 C# 程序员应该很少遇到这些情况.首先,我们这里所说的性能特征在几十纳秒的数量级上;大多数 LOB 应用程序的性能要求以秒或分钟为单位,而不是以纳秒为单位.其次,大多数 LOB C# 应用程序只需少量线程即可完成工作.第三,共享内存是一个坏主意,也是导致许多错误的原因;使用工作线程的 LOB 应用程序不应直接使用线程,而应使用任务并行库安全地指示工作线程执行计算,然后返回结果.考虑在 C# 5.0 中使用新的 await 关键字来促进基于任务的异步,而不是直接使用线程.

As an editorial aside, I note that it should be rare for normal line-of-business C# programmers to be in any of these situations. First, the performance characteristics we are talking about here are on the order of tens of nanoseconds; most LOB applications have performance requirements measured in seconds or minutes, not in nanoseconds. Second, most LOB C# applications can do their work with only a small number of threads. Third, shared memory is a bad idea and a cause of many bugs; LOB applications which use worker threads should not use threads directly, but rather use the Task Parallel Library to safely instruct worker threads to perform calculations, and then return the results. Consider using the new await keyword in C# 5.0 to facilitate task-based asynchrony, rather than using threads directly.

在 LOB 应用程序中使用任何 volatile 都是一个很大的危险信号,应该由专家进行严格审查,最好消除,以支持更高级别、更危险的做法.

Any use of volatile in a LOB application is a big red flag and should be heavily reviewed by experts, and ideally eliminated in favour of a higher-level, less dangerous practice.

lock 将阻止指令重新排序.

lock will prevent instruction reordering.

在 C# 规范中,锁被描述为代码中的一个特殊点,这样可以保证在进入和离开锁时以特定方式对某些特殊的副作用进行排序.

A lock is described by the C# specification as being a special point in the code such that certain special side effects are guaranteed to be ordered in a particular way with respect to entering and leaving the lock.

volatile 因为会强制 CPU 始终从内存中读取值(然后不同的 CPU/内核不会缓存它,也不会看到旧值).

volatile because will force CPU to always read value from memory (then different CPUs/cores won't cache it and they won't see old values).

您所描述的是有关如何实现 volatile 的实现细节;没有要求通过放弃缓存并返回主内存来实现易失性.规范中详细说明了 volatile 的要求.

What you are describing is implementation details for how volatile could be implemented; there is not a requirement that volatile be implemented by abandoning caches and going back to main memory. The requirements of volatile are spelled out in the specification.

互锁操作在单个原子(快速)操作中执行更改 + 赋值.

Interlocked operations perform change + assignment in a single atomic (fast) operation.

我不清楚你为什么在atomic"后面加上括号fast";快速"不是原子"的同义词.

It is not clear to me why you have parenthesized "fast" after "atomic"; "fast" is not a synonym for "atomic".

锁如何防止缓存问题?

再一次:锁定被记录为代码中的一个特殊事件;编译器需要确保其他特殊事件相对于锁具有特定顺序.编译器如何选择实现这些语义是一个实现细节.

Again: lock is documented as being a special event in the code; a compiler is required to ensure that other special events have a particular order with respect to the lock. How the compiler chooses to implement those semantics is an implementation detail.

是否在临界区隐含了内存屏障?

Is it implicit a memory barrier in a critical section?

实际上是的,锁引入了完整的围栏.

In practice yes, a lock introduces a full fence.

可变变量不能是局部的

正确.如果您从两个线程访问本地,那么本地必须是一个特殊的本地:它可以是委托的封闭外部变量,或者在异步块中,或者在迭代器块中.在所有情况下,本地实际上都是作为一个场来实现的.如果你想让这样的东西变得易变,那么不要使用匿名方法、异步块或迭代器块之类的高级功能!这是混合 C# 编码的最高级别和最低级别,这是一件非常奇怪的事情.编写您自己的闭包类,并根据需要使字段可变.

Correct. If you are accessing a local from two threads then the local must be a special local: it could be a closed-over outer variable of a delegate, or in an async block, or in an iterator block. In all cases the local is actually realized as a field. If you want such a thing to be volatile then do not use high-level features like anonymous methods, async blocks or iterator blocks! That is mixing the highest level and the lowest level of C# coding and that is a very strange thing to do. Write your own closure class and make the fields volatile as you see fit.

我从埃里克·利珀特 (Eric Lippert) 那里读到了一些关于此的内容,但我现在找不到那个帖子,我不记得他的回答.

I read something from Eric Lippert about this but I can't find that post now and I don't remember his answer.

好吧,我也不记得了,所以我在搜索引擎中输入了Eric Lippert 为什么局部变量不能是 volatile".这让我想到了这个问题:

Well I don't remember it either, so I typed "Eric Lippert Why can't a local variable be volatile" into a search engine. That took me to this question:

为什么在 C# 中局部变量不能是 volatile?

也许这就是你的想法.

这让我觉得它们不是通过 Interlocked.CompareExchange() 和朋友实现的.

This makes me think they're not implemented with an Interlocked.CompareExchange() and friends.

C# 将 volatile 字段实现为 volatile 字段.易失性字段是 CLR 中的一个基本概念;CLR 如何实现它们是 CLR 的实现细节.

C# implements volatile fields as volatile fields. Volatile fields are a fundamental concept in the CLR; how the CLR implements them is an implementation detail of the CLR.

它们有什么不同?

我不明白这个问题.

例如在这段代码中,什么 volatile 修饰符会做什么?

What volatile modifier will do for example in this code?

++_volatileField;

它没有任何帮助,所以不要这样做.波动性和原子性是完全不同的东西.对 volatile 字段进行正常增量不会使增量变为原子增量.

It does nothing helpful, so don't do that. Volatility and atomicity are completely different things. Doing a normal increment on a volatile field does not make the increment into an atomic increment.

此外,编译器(除了警告)会在这里做什么:

Moreover what compiler (beside warnings) will do here:

如果被调用的方法引入了栅栏,C# 编译器真的应该抑制该警告,就像这个一样.我从来没有设法把它放到编译器中.希望团队有一天会.

The C# compiler really ought to suppress that warning if the method being called introduces a fence, as this one does. I never managed to get that into the compiler. Hopefully the team will someday.

volatile 字段将以原子方式更新.增量将引入栅栏,因此可以缓解跳过易失性半栅栏的事实.

The volatile field will be updated in an atomic manner. A fence will be introduced by the increment, so the fact that the volatile half-fences are skipped is mitigated.

非易失性字段怎么可能?

How is it possible for non volatile fields?

这是 CLR 的实现细节.

That's an implementation detail of the CLR.

它们是否也意味着障碍?

Does they imply barriers too?

是的,互锁操作引入了障碍.同样,这是一个实现细节.

Yes, the interlocked operations introduce barriers. Again, this is an implementation detail.

这会不会对性能造成很大影响(与 volatile 相比)?

Doesn't this hurt performance a lot (compared to volatile)?

首先,将损坏代码的性能与工作代码的性能进行比较是浪费时间.

First off, comparing the performance of broken code to working code is a waste of time.

其次,如果您确实想浪费时间,那么您完全有能力衡量自己的表现.双向编写代码,拿出秒表,单向运行一万亿次,你就会知道哪个更快.

Second, if you do feel like wasting time, you are perfectly capable of measuring the performance of each yourself. Write the code both ways, get out a stopwatch, run it a trillion times each way, and you'll know which is faster.

如果 volatile 不意味着障碍,但其他人可以,那么为什么我们不能将它们用作局部变量?

If volatile doesn't imply barriers but others do then why we can't use them as on local variables?

我什至无法理解这个问题.

I can't even begin to make sense of this question.

这篇关于volatile 变量有用吗?如果是,那么什么时候?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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