Tag: volatile

易变性和只读是否相互排斥?

假设我正在设计一个包装内部集合的线程安全类: public class ThreadSafeQueue { private readonly Queue _queue = new Queue(); public void Enqueue(T item) { lock (_queue) { _queue.Enqueue(item); } } // … } 基于我的另一个问题 ,上面的实现是错误的,因为当它的初始化与其使用同时执行时可能会出现种族危险: ThreadSafeQueue tsqueue = null; Parallel.Invoke( () => tsqueue = new ThreadSafeQueue(), () => tsqueue?.Enqueue(5)); 上面的代码是可接受的非确定性的:该项目可能会或可能不会入队。 但是,在当前的实现中,它也被破坏了,并且可能引起不可预测的行为,例如抛出IndexOutOfRangeException , NullReferenceException ,多次排队同一项,或者陷入无限循环。 这是因为Enqueue调用可能在将新实例分配给局部变量tsqueue之后但在内部_queue字段的初始化完成(或似乎完成)之前运行。 Per Jon Skeet : Java内存模型不能确保构造函数在将新对象的引用分配给实例之前完成 。 Java内存模型经历了1.5版的重新加工,但是在没有volatile变量的情况下,双重检查锁定仍然被破坏( […]

使闭包捕获的变量变为volatile

闭包捕获的变量如何与不同的线程交互? 在下面的示例代码中,我想将totalEvents声明为volatile,但C#不允许这样做。 (是的,我知道这是不好的代码,这只是一个例子) private void WaitFor10Events() { volatile int totalEvents = 0; // error CS0106: _someEventGenerator.SomeEvent += (s, e) => totalEvents++; while(totalEvents < 10) Thread.Sleep(100); } 编辑 :人们似乎忽略了我的问题。 我知道我不能在本地变量上使用volatile 。 我也知道示例代码很糟糕,可能以其他方式实现,因此我的“坏代码”免责声明。 这只是为了说明问题。 无论如何,似乎没有办法强制将volatile语义强加到捕获的局部变量上,所以我将实现一种不同的方式。 谢谢你的答案,无论如何我已经学到了很多有用的东西。 🙂

volatile是否会阻止引入读取或写入?

在C#中, volatile关键字分别确保读取和写入具有获取和释放语义。 但是,是否有关于引入读取或写入的内容? 例如: volatile Thing something; volatile int aNumber; void Method() { // Are these lines… var local = something; if (local != null) local.DoThings(); // …guaranteed not to be transformed into these by compiler, jitter or processor? if (something != null) something.DoThings(); // <– Second read! // Are these lines… if (aNumber == […]

易失性字段:如何实际获取字段的最新写入值?

考虑以下示例: private int sharedState = 0; private void FirstThread() { Volatile.Write(ref sharedState, 1); } private void SecondThread() { int sharedStateSnapshot = Volatile.Read(ref sharedState); Console.WriteLine(sharedStateSnapshot); } 直到最近,我的印象是,只要FirstThread()确实在SecondThread()之前执行,该程序就无法输出除1之外的任何内容。 但是,我现在的理解是: Volatile.Write()发出一个发布栏。 这意味着在将1分配给sharedState之后,可能不会发生先前的加载或存储(按程序顺序)。 Volatile.Read()发出一个获取围栏。 这意味着在将sharedState复制到sharedStateSnapshot之前不会发生后续加载或存储(按程序顺序)。 或者,换句话说: 当sharedState实际发布到所有处理器核心时,该写入之前的所有内容也将被释放,并且, 获取地址sharedStateSnapshot的值时; 必须已经获得了sharedState 。 如果我的理解是正确的,那么如果FirstThread()的写入尚未被释放,则没有什么可以阻止获取sharedState是“陈旧的”。 如果这是真的,我们怎样才能真正确保(假设最弱的处理器内存模型,如ARM或Alpha),程序将始终打印1 ? (或者我在某个地方的心理模型中犯了错误?)

锁定变量应该声明为volatile吗?

我有以下Lock声明: private readonly object ownerLock_ = new object(); lock (ownerLock_) { } 我应该为我的锁变量使用volatile关键字吗? private readonly volatile object ownerLock_ = new object(); 在MSDN上我看到它通常用于没有锁定访问的字段,所以如果我使用Lock我不需要使用volatile? 来自MSDN : volatile修饰符通常用于多个线程访问的字段,而不使用lock语句来序列化访问。

Monitor.Wait确保重新读取字段吗?

人们普遍接受(我相信!)一个lock会强制重新加载字段中的任何值(基本上充当内存屏障或栅栏 – 我在这方面的术语有点松散,我害怕),结果是,只能在lock内访问的字段本身不需要是volatile 。 (如果我错了,就说!) 这里提出了一个很好的评论,质疑如果代码执行Wait()是否同样如此 – 即一旦它是Pulse() d,它将从内存重新加载字段,还是它们可能在寄存器(等)中。 或者更简单:字段是否需要是volatile以确保在Wait()之后恢复时获得当前值? 看着reflection器, Wait调用ObjWait ,这是一个managed internalcall调用managed internalcall (与Enter相同)。 有问题的情景是: bool closing; public bool TryDequeue(out T value) { lock (queue) { // arbitrary lock-object (a private readonly ref-type) while (queue.Count == 0) { if (closing) { // <==== (2) access field here value = default(T); return false; } […]

certificate代码示例失败,没有volatile

下面是一个C#代码示例,它是一个破坏的Java代码的逐字翻译(已certificate破坏(即第二个线程可能无法观察到sharedValue值的变化)至少在Mac OS X 10.9,Java 1.8(64位)上),Arrandale(1插槽x 2核x 2 HT = 4个HW线程)): using System; using System.Threading; class ThreadTest { /* volatile */ private int sharedValue; private void RunAsync() { while (this.sharedValue == 0); } private bool Test() { Thread t = new Thread(this.RunAsync); t.IsBackground = true; t.Start(); Thread.Sleep(10); // Yes I’m aware the operation is not atomic […]

CancellationTokenSource vs. volatile boolean

在一个易失性布尔字段上使用CancellationTokenSource来表示Task完成是否有任何好处?

C#中的Volatile和Thread.MemoryBarrier

为了实现multithreading应用程序的无锁代码 ,我使用了volatile变量, 理论上 : volatile关键字用于确保所有线程都能看到volatile变量的最新值; 因此,如果线程A更新变量值,并且线程B在更新发生之后读取该变量,它将看到最近从线程A写入的最新值。正如我在Nutshell书中的C#4.0中读到的那样,这是不正确的,因为 应用volatile不会阻止写入后读取交换。 可以通过在每次获取volatile变量之前放置Thread.MemoryBarrier()来解决这个问题: private volatile bool _foo = false; private void A() { //… Thread.MemoryBarrier(); if (_foo) { //do somthing } } private void B() { //… _foo = true; //… } 如果这解决了问题; 考虑我们有一个while循环,它依赖于其中一个条件的值; 在while循环之前放置Thread.MemoryBarrier()是解决问题的正确方法吗? 例: private void A() { Thread.MemoryBarrier(); while (_someOtherConditions && _foo) { // do somthing. } […]

内存障碍是否可以保证C#的新读取?

如果我们在C#中有以下代码: int a = 0; int b = 0; void A() // runs in thread A { a = 1; Thread.MemoryBarrier(); Console.WriteLine(b); } void B() // runs in thread B { b = 1; Thread.MemoryBarrier(); Console.WriteLine(a); } MemoryBarriers确保写入指令在读取之前发生。 但是,是否可以保证在另一个线程上读取一个线程的写入? 换句话说,是否保证至少有一个线程打印1或两个线程都可以打印0 ? 我知道已经存在几个与C#中的“ MemoryBarrier ”和MemoryBarrier相关的问题,就像这样和这个 。 但是,它们中的大多数都处理写入释放和读取 – 获取模式。 在这个问题中发布的代码,非常特定于在保持指令保持有序的情况下是否保证通过读取来查看写入。