C#值类型分配是原子的吗?

时间:2018-12-03 20:08:44

标签: c# thread-safety atomic value-type 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();
    }
}

这样的程序会遭受高级数据竞争吗?甚至是数据竞赛

我想在这里知道的是:v是否可能包含:

  • 由于可能的数据争夺而导致的垃圾值
  • 混合元素X,Y或Z,它们既指分配前的pos,又指分配后的pos。例如,如果pos =(1,1,1),然后为pos分配了新值(2,2,2),则v =(1,2,2)可以吗?

1 个答案:

答案 0 :(得分:2)

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

不能保证复制结构是原子操作。如C# language specification中所述:

  

变量引用的原子性

     

以下数据类型的读写是原子的: bool char ,    byte sbyte short ushort uint int < / em>,浮动引用类型。在   此外,对具有基本类型的枚举类型进行读写   前面的列表也是原子的。读写其他类型的   包括 long ulong double decimal ,以及用户定义的   类型,不保证是原子的。除了图书馆   为此目的设计的功能,不能保证原子性   读取-修改-写入,例如递增或递减。


因此,是的,当一个线程正在从结构存储位置复制数据的过程中,另一个线程出现并开始将新数据从另一个结构复制到该存储位置。因此,从存储位置复制线程可能最终会复制旧数据和新数据。


附带说明一下,由于您的一个线程如何写入变量以及另一个线程如何使用该变量,您的代码还会遭受其他并发问题。 (用户 acelent 的另一个问题的答案在技术细节上对此进行了很好的解释,因此我将仅引用它:https://stackoverflow.com/a/46695456/2819245。)可以通过封装对此类“ lock块中的“线程交叉”变量。作为lock的替代方法,并且关于基本数据类型,您还可以使用Interlocked类提供的方法以线程安全的方式访问线程交叉的变量/字段(两者之间交替但是,对于相同的线程交叉变量,lockInterlocked方法不是一个好主意。