【发布时间】:2020-05-28 17:41:14
【问题描述】:
我正在查看 System.Threading.Tasks(.NET 标准 2.0)中 Task 的一些实现细节,我发现了这段有趣的代码:
internal volatile int m_stateFlags;
...
public bool IsCompleted
{
get
{
int stateFlags = m_stateFlags; // enable inlining of IsCompletedMethod by "cast"ing away the volatiliy
return IsCompletedMethod(stateFlags);
}
}
// Similar to IsCompleted property, but allows for the use of a cached flags value
// rather than reading the volatile m_stateFlags field.
private static bool IsCompletedMethod(int flags)
{
return (flags & TASK_STATE_COMPLETED_MASK) != 0;
}
通过阅读 C# 参考指南,我了解到 volatile 是为了防止编译器/运行时/硬件优化导致对字段的读/写重新排序。为什么在这种特定情况下将字段指定为 volatile 只是为了在通过将字段分配给变量来读取字段时忽略波动性?
这似乎是有意的,但我不清楚意图背后的原因。 另外,“抛弃”波动性是一种常见的做法吗?在哪些情况下我想这样做,而我绝对想避免这样做?
非常感谢任何有助于我更清楚地理解这段代码的信息。
谢谢,
【问题讨论】:
-
这是非常顽皮的代码,肯定是在 .NET 4.0 的 Volatile.Read() 稳定之前编写的。这是 Thread.VolatileRead() 的错误修复,它错误地占用了完整的内存屏障。抖动在具有弱内存模型(arm、itanium)的处理器上为 volatile 关键字提供额外语义,提供读取获取语义。在这个属性 getter 中没有意义,但在其他两个地方使用它。顺便说一句,OptionsMethod() 也是如此。除了高成本之外,它还避免了多次测试旗帜时的比赛。一致性是一件好事。
标签: c# .net task task-parallel-library volatile