在.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是否可能包含:
答案 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
类提供的方法以线程安全的方式访问线程交叉的变量/字段(两者之间交替但是,对于相同的线程交叉变量,lock
和Interlocked
方法不是一个好主意。