3

值类型的分配是否在.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 = (1,1,1) 然后为 pos 分配新值 (2,2,2) 可以 v = (1,2,2) 吗?
4

1 回答 1

8

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

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

变量引用的原子性

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


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


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

于 2018-12-03T21:10:47.423 回答