C# 结构体线程安全吗?

问题描述 投票:0回答:6

C# 结构体是线程安全的吗?

例如,如果有:

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 的属性是线程安全的吗?

c# concurrency struct thread-safety parallel-processing
6个回答
12
投票

嗯 - 最佳实践是结构应该始终是不可变的(除了一些非常特定的场景,甚至存在风险)。并且不可变数据始终是线程安全的。因此,如果您遵循最佳实践并做到了这一点:

struct Data
{
    readonly int _number;
    public int Number { get { return _number; } }

    public Data(int number) { _number = number; }
}

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

另请注意,原子性规则适用,因此即使对

DadData.TheData
的单个读取或更新也不能被认为是线程安全的,即使 with 是不可变的结构。您可以(特别是对于超大结构)让一个线程读取该结构,而另一个线程重写它;如果没有同步,不好的事情将会发生(最终)。


11
投票

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

但是,结构中的按值复制语义与此对话有很大的相关性。

如果您传递结构并以某种方式将它们分配给变量或按值传递参数(无 ref 或 out 关键字),则正在使用 copy

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

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


1
投票
A

struct

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

此外,可变结构也是代码异味。是否有某些特殊原因需要它成为

struct

 而不是 
class
?您需要此数据的值类型语义吗?


1
投票
不同线程对可变结构体不同成员的直接读写不会互相干扰。不同线程通过互锁方法对同一成员的访问将根据这些方法的语义进行操作。这些事实可能允许可变结构允许线程安全行为。

保存结构的可变存储位置除了彻底替换之外不提供任何突变方式,不提供任何线程安全性,除非结构保存单个 32 位整数或单个对象引用,尝试读取此类(单项)结构存储位置在写入的同时保证读取完全旧的数据或全新的数据。请注意,不可能将任何 Interlocked 方法与不可变结构一起使用 - 即使是仅包含单个整数或对象引用的结构。


1
投票
不,他们不是。 我创建了一个非常简单的应用程序来查看 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<Task> allTasks = new List<Task>(); 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()); } } }
    

-2
投票
不。为什么它是线程安全的?这只是数据。它不会通过魔法变得线程安全。

© www.soinside.com 2019 - 2024. All rights reserved.