具有易失性或锁定性的财产 [英] Property with Volatile or Lock

查看:69
本文介绍了具有易失性或锁定性的财产的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个带有后备字段的属性,我想使线程安全(获取并设置). 除了设置和返回之外,get and set方法没有逻辑.

I have a property with a backing field which I want to make thread safe (get and set). The get and set method has no logic except the setting and returning.

我认为有两种方法可以将逻辑封装在属性self中(易失性和锁定). 我对这两者的理解是正确的还是犯错了?

I think there are two ways to capsule the logic in the property self (volatile and lock). Is my understanding of the two's correct or have I make any mistakes?

下面是我的示例:

    public class ThreadSafeClass
    {
        // 1. Volatile Example:

        private volatile int _processState_1;
        public int ProcessState_1
        {
            get { return _processState_1; }
            set { _processState_1 = value; }
        }

        // 2. Locking Example:

        private readonly object _processState_2Lock = new object();
        private int _processState_2;
        public int ProcessState_2
        {
            get
            {
                lock (_processState_2Lock)
                {
                    return _processState_2;
                }
            }
            set
            {
                lock (_processState_2Lock)
                {
                    _processState_2 = value;
                }
            }
        }
    }

推荐答案

有关更多信息,请参见 J.Albahari的绝佳站点:

For mor information see the great site by J. Albahari:

同步结构可以分为四类:

Synchronization constructs can be divided into four categories:

简单的阻止方法:

这些等待另一个线程完成或等待一段时间. SleepJoinTask.Wait是简单的阻止方法.

These wait for another thread to finish or for a period of time to elapse. Sleep, Join, and Task.Wait are simple blocking methods.

锁定结构:

这些限制了一次可以执行某些活动或执行一段代码的线程数.排它锁定结构是最常见的-一次仅允许一个线程,并且允许竞争线程访问公共数据而不会互相干扰.标准排它锁定构造为lock(Monitor.Enter/Monitor.Exit),MutexSpinLock.非唯一的locking构造是SemaphoreSemaphoreSlimreader/writer锁.

These limit the number of threads that can perform some activity or execute a section of code at a time. Exclusive locking constructs are most common — these allow just one thread in at a time, and allow competing threads to access common data without interfering with each other. The standard exclusive locking constructs are lock (Monitor.Enter/Monitor.Exit), Mutex, and SpinLock. The nonexclusive locking constructs are Semaphore, SemaphoreSlim, and the reader/writer locks.

信令构造:

这允许线程暂停直到接收到来自另一个线程的通知为止,从而避免了低效率的轮询.常用的信号设备有两种:事件等待句柄和监视器的等待/脉冲方法. Framework 4.0引入了CountdownEventBarrier类.

These allow a thread to pause until receiving a notification from another, avoiding the need for inefficient polling. There are two commonly used signaling devices: event wait handles and Monitor’s Wait/Pulse methods. Framework 4.0 introduces the CountdownEvent and Barrier classes.

无阻塞同步结构:

这些通过调用处理器原语来保护对公共字段的访问. CLR和C#提供以下非阻塞构造:Thread.MemoryBarrierThread.VolatileReadThread.VolatileWritevolatile关键字和Interlocked类.

These protect access to a common field by calling upon processor primitives. The CLR and C# provide the following nonblocking constructs: Thread.MemoryBarrier, Thread.VolatileRead, Thread.VolatileWrite, the volatile keyword, and the Interlocked class.

volatile关键字:

The volatile keyword:

volatile关键字指示编译器在对该字段的每次读取中生成一个获取围栏,并在对该字段的每次写入中生成一个释放围栏.获取栅栏可防止其他读取/写入在栅栏之前移动;释放栅栏可防止其他读取/写入在栅栏之后移动.这些半栅栏"比完整的栅栏要快,因为它们为运行时和硬件提供了更大的优化范围.

The volatile keyword instructs the compiler to generate an acquire-fence on every read from that field, and a release-fence on every write to that field. An acquire-fence prevents other reads/writes from being moved before the fence; a release-fence prevents other reads/writes from being moved after the fence. These "half-fences" are faster than full fences because they give the run-time and hardware more scope for optimization.

碰巧的是,英特尔的X86和X64处理器始终将获取栅栏应用于读取,将释放栅栏应用于写入(无论是否使用volatile关键字),因此,如果您使用这些关键字,则此关键字对硬件没有任何影响处理器.但是,volatile确实会影响编译器和CLR以及64位AMD和(在更大程度上)Itanium处理器执行的优化.这意味着,依靠您的客户端运行特定类型的CPU,您再也无法放心了.

As it happens, Intel’s X86 and X64 processors always apply acquire-fences to reads and release-fences to writes — whether or not you use the volatile keyword — so this keyword has no effect on the hardware if you’re using these processors. However, volatile does have an effect on optimizations performed by the compiler and the CLR — as well as on 64-bit AMD and (to a greater extent) Itanium processors. This means that you cannot be more relaxed by virtue of your clients running a particular type of CPU.

将volatile应用于字段的效果可总结如下:

The effect of applying volatile to fields can be summarized as follows:

First instruction   Second instruction  Can they be swapped?
Read                Read                No
Read                Write               No
Write               Write               No (The CLR ensures that write-write operations are never swapped, even without the volatile keyword)
Write               Read                Yes!

请注意,应用volatile并不会阻止写入和读取之间的交换,这可能会引起脑筋急转弯. Joe Duffy通过以下示例很好地说明了此问题:如果Test1Test2在不同的线程上同时运行,则a和b的最终值都可能为0(尽管在y):

Notice that applying volatile doesn’t prevent a write followed by a read from being swapped, and this can create brainteasers. Joe Duffy illustrates the problem well with the following example: if Test1 and Test2 run simultaneously on different threads, it’s possible for a and b to both end up with a value of 0 (despite the use of volatile on both x and y):

class IfYouThinkYouUnderstandVolatile
{
  volatile int x, y;

  void Test1()        // Executed on one thread
  {
    x = 1;            // Volatile write (release-fence)
    int a = y;        // Volatile read (acquire-fence)
    ...
  }

  void Test2()        // Executed on another thread
  {
    y = 1;            // Volatile write (release-fence)
    int b = x;        // Volatile read (acquire-fence)
    ...
  }
}

MSDN文档指出,使用volatile关键字可确保始终在字段中提供最新值.这是不正确的,因为正如我们所看到的,写入和读取之后可以重新排序.

The MSDN documentation states that use of the volatile keyword ensures that the most up-to-date value is present in the field at all times. This is incorrect, since as we’ve seen, a write followed by a read can be reordered.

这为避免出现波动提供了一个强有力的案例:即使您理解了此示例中的精妙之处,其他从事您代码工作的开发人员也会理解吗? Test1Test2中的两个分配之间的完全隔离(或锁定)可以解决此问题.

This presents a strong case for avoiding volatile: even if you understand the subtlety in this example, will other developers working on your code also understand it? A full fence between each of the two assignments in Test1 and Test2 (or a lock) solves the problem.

传递引用参数或捕获的局部变量不支持volatile关键字:在这种情况下,您必须使用VolatileReadVolatileWrite方法.

The volatile keyword is not supported with pass-by-reference arguments or captured local variables: in these cases you must use the VolatileRead and VolatileWrite methods.

这篇关于具有易失性或锁定性的财产的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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