C#结构是否安全?

C#struct是否是线程安全的?

例如,如果有:

struct Data { int _number; public int Number { get { return _number; } set { _number = value; } } public Data(int number) { _number = number; } } 

在另一种类型:

 class DadData { public Data TheData { get; set; } } 

是名为TheData的属性,线程安全吗?

不,.NET中的结构本质上不是线程安全的。

然而,结构的复制语义与这种对话有很大关系。

如果您传递结构并以某种方式将它们分配给变量或按值传递参数(没有引用或输出关键字),则使用副本

当然,这意味着对副本所做的任何更改都不会反映在原始结构中,但是在传递它们时需要注意的事项。

如果您以不涉及按值复制语义的方式直接访问结构(例如,访问静态字段,这是结构的类型,并且正如Marc Gravel在其答案中指出的那样 ,还有许多其他方式)跨多个线程,然后你必须考虑实例的线程安全性。

好 – 最好的做法是结构应该总是(除了在一些非常具体的情况下,即使那时有风险)也是不可改变的。 并且不可变数据始终是线程安全的。 所以如果你遵循最佳实践并做到了这一点:

 struct Data { readonly int _number; public int Number { get { return _number; } } public Data(int number) { _number = number; } } 

好的; 这是线程安全的。 在所有其他情况下,答案是“可能不是”。

另请注意,primefaces性规则适用,因此即使使用不可变结构,也不能假定对DadData.TheData的单个读取或更新是线程安全的。 您可以(特别是对于超大结构)有一个线程读取结构,而另一个线程重写它; 没有同步会发生坏事(最终)。

struct不再是普通字段或变量的线程安全性。 如果您至少有一个线程修改它,并且至少有一个线程同时以任何方式触摸它,您可能最终会出现意外/未定义的行为。

此外,可变结构是代码味道。 是否有某些特殊原因需要它作为struct而不是class ? 您是否需要此数据的值类型语义?

不同线程对可变结构的不同成员的直接读写不会相互干扰。 不同线程通过Interlocked方法对同一成员的访问将根据这些方法的语义来表现。 这些事实可能允许可变结构允许线程安全行为。

除了在结构中包含单个32位整数或单个对象引用的情况下,尝试读取这样的结构(除了在结构中保存单个32位整数或单个对象引用的情况下)之外,保持不提供任何突变的结构的可变存储位置不提供任何线程安全性。单个项目结构存储位置在写入的同时保证读取完全旧数据或全新数据。 请注意,不可能将任何Interlocked方法与不可变结构一起使用 – 甚至是只包含单个整数或对象引用的结构。

不,他们不是。 我创建了非常简单的应用程序,以查看10/10生产者/消费者线程是否访问相同的结构变量 最后你会看到Debugger.Break(); 会受到打击。 银行余额绝不应低于0。

 namespace StructThreadSafe { class Program { struct BankBalance { public decimal Balance { get; set; } } static void Main(string[] args) { BankBalance bankBalance = new BankBalance(); bankBalance.Balance = 100; List allTasks = new List(); for (int q = 0; q < 10; q++) { Task producer = new Task(() => { for (int i = 0; i < 1000; i++) { if (bankBalance.Balance < 0) { if (Debugger.IsAttached) { Debugger.Break(); } } bankBalance.Balance += 5; Console.WriteLine("++Current Balance: " + bankBalance.Balance); System.Threading.Thread.Sleep(100); } }); allTasks.Add(producer); } for (int w = 0; w < 10; w++) { Task consumer = new Task(() => { for (int i = 0; i < 1000; i++) { if (bankBalance.Balance < 0) { if (Debugger.IsAttached) { Debugger.Break(); } } if (bankBalance.Balance > 15) { bankBalance.Balance -= 15; Console.WriteLine("--Current Balance: " + bankBalance.Balance); } else { Console.WriteLine("**Current Balance below minimum: " + bankBalance.Balance); } System.Threading.Thread.Sleep(100); } }); allTasks.Add(consumer); } allTasks.ForEach(p => p.Start()); Task.WaitAll(allTasks.ToArray()); } } } 

不,为什么它是线程安全的? 这只是数据。 它不会被魔法变成线程安全的。