C# 值类型赋值是原子的吗?

And*_*tos 3 c# atomic value-type thread-safety tearing

值类型的分配是否在 .Net 中被认为是原子的?

例如,考虑以下程序:

struct Vector3
{
    public float X { get; private set; }
    public float Y { get; private set; }
    public float Z { get; private set; }


    public Vector3(float x, float y, float z)
    {
        this.X = x;
        this.Y = y;
        this.Z = z;
    }

    public Vector3 Clone()
    {
        return new Vector3(X, Y, Z);
    }

    public override String ToString()
    {
        return "(" + X + "," + Y + "," + Z + ")";
    }
}

class Program
{
    private static Vector3 pos = new Vector3(0,0,0);

    private static void ReaderThread()
    {
        for (int i = 0; i < int.MaxValue; i++)
        {
            Vector3 v = pos;
            Console.WriteLine(v.ToString());
            Thread.Sleep(200);
        }

    }

    private static void WriterThread()
    {
        for (int i = 1; i < int.MaxValue; i++)
        {
            pos = new Vector3(i, i, i);
            Thread.Sleep(200);
        }
    }


    static void Main(string[] args)
    {
        Thread w = new Thread(WriterThread);
        Thread r = new Thread(ReaderThread);

        w.Start();
        r.Start();
    }
}
Run Code Online (Sandbox Code Playgroud)

像这样的程序会受到高级数据竞争的影响吗?甚至是数据竞赛

我想知道的是: v 是否有可能包含:

  • 由于可能的数据竞争而产生的垃圾值
  • 指代赋值前的 pos 和赋值后的 pos 的混合组件 X、Y 或 Z。例如,如果 pos = (1,1,1) 然后为 pos 分配新值 (2,2,2) 可以 v = (1,2,2) 吗?

小智 8

结构是值类型。如果将结构体分配给变量/字段/方法参数,则整个结构体内容将从源存储位置复制到变量/字段/方法参数的存储位置(每种情况下的存储位置都是结构本身)。

复制结构不能保证是原子操作。正如C# 语言规范中所写:

变量引用的原子性

以下数据类型的读取和写入是原子的:boolcharbytesbyteshortushortuintintfloat引用类型。此外,具有前面列表中的基础类型的枚举类型的读取和写入也是原子的。读取和写入其他类型,包括longulongdoubledecimal以及用户定义的类型,不能保证是原子的。除了为此目的设计的库函数外,无法保证原子读-修改-写,例如在递增或递减的情况下。


所以是的,当一个线程正在结构存储位置复制数据的过程中,另一个线程出现并开始将新数据从另一个结构复制该存储位置时,可能会发生这种情况。因此,从存储位置复制的线程最终可能会复制旧数据和新数据的混合。


作为旁注,由于您的一个线程如何写入变量以及另一个线程如何使用该变量,您的代码还可能会遇到其他并发问题。(用户acelent对另一个问题的回答在技​​术细节上很好地解释了这一点,因此我将参考它:https : //stackoverflow.com/a/46695456/2819245)您可以通过封装此类的任何访问来避免此类问题块中的“线程交叉”变量lock。作为替代lock,而对于基本数据类型,你也可以使用通过所提供的方法Interlocked在一个线程安全的方式(在两者之间类来访问线程交叉变量/场交替lockInterlocked 但是,用于相同线程交叉变量的方法不是一个好主意)。