在C#中赋值给volatile变量

Vya*_*ava 3 c# volatile

我对C#的理解说(感谢Jeff Richter和Jon Skeet),任务是"原子的".什么不是当我们混合读写(递增/递减),因此我们需要使用Interlocked上的方法.如果只有Read&assign,那么这两个操作都是原子的吗?

public class Xyz
{
    private volatile int _lastValue;
    private IList<int> AvailableValues { get; set; }
    private object syncRoot = new object();
    private Random random = new Random();

    //Accessible by multiple threads
    public int GetNextValue() //and return last value once store is exhausted
    {
        //...

        var count = 0;
        var returnValue = 0;

        lock (syncRoot)
        {
            count = AvailableValues.Count;
        }

        if (count == 0)
        {
            //Read... without locking... potential multiple reads
            returnValue = _lastValue;
        }
        else
        {

            var toReturn = random.Next(0, count);

            lock (syncRoot)
            {
                returnValue = AvailableValues[toReturn];
                AvailableValues.RemoveAt(toReturn);
            }
            //potential multiple writes... last writer wins
            _lastValue = returnValue;
         }

        return returnValue;

    }
Run Code Online (Sandbox Code Playgroud)

Eri*_*ert 18

我对C#的理解说(感谢Jeff Richter和Jon Skeet),任务是"原子的".

一般来说,分配不是原子的.C#规范仔细调出了保证原子的东西.见5.5节:

以下数据类型的读取和写入是原子的:bool,char,byte,sbyte,short,ushort,uint,int,float和reference类型.此外,在先前列表中具有基础类型的枚举类型的读取和写入也是原子的.其他类型的读写,包括long,ulong,double和decimal,以及用户定义的类型,不保证是原子的.

(重点补充.)

如果只有Read&assign,那么这两个操作都是原子的吗?

同样,第5.5节回答了你的问题:

不保证原子读 - 修改 - 写


Mar*_*ell 11

volatile实际上与缓存(寄存器等)更相关; 与volatile您知道该值实际写入到/读取从内存中直接(这实际上不是总是这样以其他方式).这允许不同的线程立即看到彼此的更新.指令重新排序还有其他微妙的问题,但这很复杂.

这里有两个"原子"的含义:

  • 是单个读取原子本身/写入原子本身(即另一个线程可以得到两个不同的两个Doubles的一半,产生一个从未存在过的数字)
  • 是一个读/写对原子/孤立一起

"本身"取决于价值的大小; 可以在一次操作中更新吗?读/写对更多地与隔离有关 - 即防止丢失更新.

在您的示例中,两个线程可以读取相同的内容_lastValue,进行计算,然后(单独)更新_lastValue.其中一个更新将丢失.实际上,我希望你想要一个lock超过读/写过程的持续时间.


Mat*_*lls 5

使用volatile关键字不会使访问线程安全,它只是确保从内存中读取变量,而不是从可能从先前读取缓存的寄存器中读取.某些体系结构会进行此优化,这会导致在您将多个线程写入同一变量时使用过时值.

为了正确同步访问,您需要拥有更宽的锁:

public class Xyz
{
    private volatile int _lastValue;
    private IList<int> AvailableValues { get; set; }
    private object syncRoot = new object();
    private Random rand = new Random();

    //Accessible by multiple threads
    public int GetNextValue() //and return last value once store is exhausted
    {
        //...

        lock (syncRoot)
        {
            var count = AvailableValues.Count;
            if(count == 0)
                return _lastValue;

            toReturn = rand.Next(0, count);
            _lastValue = AvailableValues[toReturn];
            AvailableValues.RemoveAt(toReturn);
        }
        return _lastValue;
    }
}
Run Code Online (Sandbox Code Playgroud)

如果性能是一个问题,您可能需要考虑使用LinkedList for AvailableValues,因为它支持O(1)删除操作.