我已经使用了volatile,我不确定它是否有必要.我很确定锁定在我的情况下会有点过分.阅读这个帖子(Eric Lippert评论)让我对使用volatile感到焦虑:什么时候应该在c#中使用volatile关键字?
我使用volatile是因为我的变量在多线程上下文中使用,其中可以同时访问/修改此变量,但是我可以在没有任何伤害的情况下松散添加(参见代码).
我添加"挥发性",以确保有不发生未命中对准:在另一个取可以在2通过在从另一个线程中间的写入被打破仅读取32可变的比特和其他32位.
我先前的假设(先前的陈述)是否真的可以发生?如果没有,"挥发性"使用是否仍然是必要的(选项属性修改可能在任何线程中发生).
看了2个第一个答案.我想坚持代码编写方式的事实,如果由于并发性我们错过了一个增量(想要从2个线程递增但结果仅由于并发而增加1)并不重要变量'_actualVersion'递增.
作为参考,这是我正在使用它的代码的一部分.仅在应用程序空闲时报告保存操作(写入磁盘).
public abstract class OptionsBase : NotifyPropertyChangedBase
{
private string _path;
volatile private int _savedVersion = 0;
volatile private int _actualVersion = 0;
// ******************************************************************
void OptionsBase_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
_actualVersion++;
Application.Current.Dispatcher.BeginInvoke(new Action(InternalSave), DispatcherPriority.ApplicationIdle);
}
// ******************************************************************
private void InternalSave()
{
if (_actualVersion != _savedVersion)
{
_savedVersion = _actualVersion;
Save();
}
}
// ******************************************************************
/// <summary>
/// Save Options
/// </summary>
private void Save()
{
using (XmlTextWriter writer = new XmlTextWriter(_path, null)) …Run Code Online (Sandbox Code Playgroud) 我最近接受了一家软件公司的采访,他问我以下问题:
你能告诉我在变量前添加volatile的含义是什么吗?你能解释一下为什么这很重要吗?
我的大多数编程知识都来自C,但是工作岗位是针对C#的(我想我可能会根据具体问题的需要添加这些信息)
我回答说它只是让编译器知道变量可以跨进程或线程使用,并且它不应该对该变量使用优化; 优化它可以恶化行为.简而言之,它是对编译器的警告.
然而,根据采访者的说法,反过来说,volatile关键字会警告操作系统,而不是编译器.
我对此感到有些困惑,所以我做了一些研究,实际上找到了相互矛盾的答案!一些消息来源说这是针对编译器的,还有其他针对操作系统的.
这是什么?它是否因语言而异?
我最近在Resharper网站上看到了以下帖子.这是对双重检查锁定的讨论,并具有以下代码:
public class Foo
{
private static volatile Foo instance;
private static readonly object padlock = new object();
public static Foo GetValue()
{
if (instance == null)
{
lock (padlock)
{
if (instance == null)
{
instance = new Foo();
instance.Init();
}
}
}
return instance;
}
private void Init()
{
...
}
}
Run Code Online (Sandbox Code Playgroud)
该帖子然后声称
如果我们假设Init()是用于初始化Foo状态的方法,那么由于内存模型不能保证读写顺序,上述代码可能无法按预期运行.因此,对Init()的调用实际上可能在变量实例处于一致状态之前发生.
这是我的问题:
我的理解是,.NET内存模型(至少从2.0开始)并没有要求instance被声明为volatile,因为lock它将提供一个完整的内存栅栏.是不是这样,还是我被误导了?
对于多个线程,是不是只对可观察的读/写重新排序?我的理解是,在一个线程上,副作用将是一致的顺序,并且lock就地防止任何其他线程观察到某些东西是不对的.我也在这里吗?
我有一些代码,在新线程上抛出异常,我需要在主线程上确认和处理.为了实现这一点,我通过使用保存抛出异常的字段来共享线程之间的状态.
我的问题是在检查null时是否需要使用锁,因为我在以下代码示例中进行操作?
public class MyClass
{
readonly object _exceptionLock = new object();
Exception _exception;
public MyClass()
{
Task.Run(() =>
{
while (CheckIsExceptionNull())
{
// This conditional will return true if 'something has gone wrong'.
if(CheckIfMyCodeHasGoneWrong())
{
lock(_exceptionLock)
{
_exception = new GoneWrongException();
}
}
}
});
}
bool CheckIsExceptionNull() // Is this method actually necessary?
{
lock (_exceptionLock)
{
return _exception == null;
}
}
// This method gets fired periodically on the Main Thread.
void RethrowExceptionsOnMainThread()
{ …Run Code Online (Sandbox Code Playgroud) 在C#中,我们知道a bool是原子的 - 那么为什么将它标记为有效volatile呢?有什么不同,什么是一个好的(甚至是实际的)用例?
bool _isPending;
Run Code Online (Sandbox Code Playgroud)
与
volatile bool _isPending; // Is this realistic, or insanity?
Run Code Online (Sandbox Code Playgroud)
我在这里和这里做了一些阅读,我正在努力确保我完全理解两者的内部运作.我想知道什么时候使用一个与另一个相比,或者只是bool足够.
我们都知道堆栈和堆的想法,但我最近读到了第三个保存数据的选项:寄存器.
我很难找到关于这种类型的好文章,我发现的是:http://www.dotnetperls.com/method-parameter,以及C的很多内容,例如:http://igoro.com/存档/易失性-关键字在-C-存储器模型解释的/
到目前为止我唯一的实际信息:每个CPU都有自己的寄存器,可用于保存数据,以尽可能快的方式访问,例如在for循环中.
据我所见,这种注册是由CLR完成的.然后我想起了这个volatile-keyword,如果我们看一下MSDN:
volatile关键字表示某个字段可能被同时执行的多个线程修改.声明为volatile的字段不受编译器优化的约束,这些优化假定由单个线程进行访问.这可确保始终在字段中显示最新值.
那么Volatile也是如此吗?它告诉CLR不要使用CPU寄存器而是堆栈/堆,它可以被所有CPU /线程访问?
我很抱歉这个令人困惑的问题,但关于这个话题的信息确实很少.
我有两个线程:一个提供更新,另一个将它们写入磁盘.只有最新的更新很重要,所以我不需要PC队列.
简而言之:
我目前正在使用专用锁对象来确保没有不一致,我想知道直接锁定标志和缓冲区有什么区别.我唯一知道的是,一个专用的锁对象需要信任,每个想要操纵标志和缓冲区的人都使用锁.
相关代码:
private object cacheStateLock = new object();
string textboxContents;
bool hasNewContents;
private void MainTextbox_TextChanged(object sender, TextChangedEventArgs e)
{
lock (cacheStateLock)
{
textboxContents = MainTextbox.Text;
hasNewContents = true;
}
}
private void WriteCache() // running continually in a thread
{
string toWrite;
while (true)
{
lock (cacheStateLock)
{
if (!hasNewContents)
continue;
toWrite = textboxContents;
hasNewContents = false;
}
File.WriteAllText(cacheFilePath, toWrite);
}
}
Run Code Online (Sandbox Code Playgroud) ARM允许重新排序加载后续存储,以便以下伪代码:
// CPU 0 | // CPU 1
temp0 = x; | temp1 = y;
y = 1; | x = 1;
可以导致temp0 == temp1 == 1(并且,这在实践中也是可观察到的).我无法理解这是怎么发生的; 似乎有序提交会阻止它(这是我的理解,它存在于几乎所有的OOO处理器中).我的理由是"在提交之前,负载必须具有其值,它在存储之前提交,并且在提交之前,存储的值不会对其他处理器可见."
我猜我的一个假设肯定是错的,并且必须遵循下列之一:
说明不需要提交一路有序.稍后的存储可以安全地提交并在之前的加载之前变得可见,只要在存储提交核心时可以保证先前的加载(以及所有中间指令)不会触发异常,并且加载的地址是保证与商店不同.
负载可以在其值已知之前提交.我不知道如何实现这一点.
商店在提交之前可以显示.也许某个内存缓冲区允许将存储转发到另一个线程的加载,即使负载先前已加入?
还有别的吗?
有许多假设的微体系结构特征可以解释这种行为,但我最好的是那些实际存在于现代弱有序CPU中的那些.
Read(Int64).NET系统类System.Threading.Volatile和的方法有什么区别System.Threading.Interlocked?
具体而言,关于(a)原子性和(b)内存排序,它们各自的保证/行为是什么。
请注意,这是关于Volatile阶级,不是的volatile(小写)的关键字。
MS docs状态:
易读方法
读取字段的值。在需要它的系统上,插入一个内存屏障,以防止处理器按以下方式重新排序内存操作:如果在代码中此方法之后出现读取或写入,则处理器无法在此方法之前移动它。
...
退货
Int64读取的值。不管处理器数量或处理器缓存状态如何,此值都是计算机中任何处理器最新写入的值。
与
Interlocked.Read(Int64)方法
返回作为原子操作加载的64位值。
似乎特别令人困惑的是,这些Volatile文档没有谈论原子性,并且这些Interlocked文档没有谈论排序/内存障碍。
旁注:作为参考:我更熟悉C ++原子API,其中原子操作也总是指定内存排序语义。
Pavel有用地提供的问题链接(和可传递链接)很好地解释了挥发性记忆屏障和原子无撕裂阅读的区别/正交性,但它们并没有解释这两个概念如何应用于这两个类别。
Volatile.Read保证原子性?Interlocked.Read(或者,真的,任何的Interlocked功能)做出的内存为了任何保证?我正在尝试编译以下代码:
internal volatile bool isRunning { get; set; }
Run Code Online (Sandbox Code Playgroud)
但是编译失败并显示错误消息,例如:“volatile 修饰符对该元素无效”。但是下面的代码会被很好地编译:
internal volatile bool _isRunning;
internal bool isRunning{
get { return _isRunning; }
set { _isRunning = value; }
}
Run Code Online (Sandbox Code Playgroud)
两个代码片段有什么区别??
c# ×8
volatile ×4
.net ×2
arm ×1
atomic ×1
boolean ×1
c ×1
clr ×1
dispatcher ×1
exception ×1
interlocked ×1
memory-model ×1
null ×1
performance ×1