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的属性,是否为线程安全?
答案 0 :(得分:9)
嗯 - 最好的做法是结构应该总是(除了在一些非常具体的情况下,即使那时有风险)也是不可改变的。不可变数据始终是线程安全的。所以,如果你遵循最佳实践并做到了这一点:
struct Data
{
readonly int _number;
public int Number { get { return _number; } }
public Data(int number) { _number = number; }
}
然后是;这是线程安全的。在所有其他情况下,答案是“可能不是”。
另请注意,原子性规则适用,因此即使对DadData.TheData
进行单个读取或更新也不能被认为是线程安全的,即使带有不可变结构也是如此。你可以(特别是对于超大结构)有一个线程读取结构,而另一个线程重写它;没有同步会发生坏事(最终)。
答案 1 :(得分:8)
不,.NET中的结构本质上不是线程安全的。
然而,结构的复制语义与这种对话有很大的关联。
如果您传递结构并以某种方式将它们分配给变量或按值传递参数(没有引用或输出关键字),则正在使用副本。
当然,这意味着对副本所做的任何更改都不会反映在原始结构中,但是在传递它们时需要注意的事项。
如果您以不涉及按值复制语义的方式直接访问结构(例如访问结构类型的静态字段,以及Marc Gravel points out in his answer,还有许多其他方法)跨多个线程,然后你必须考虑实例的线程安全性。
答案 2 :(得分:1)
struct
不再是普通字段或变量的线程安全。如果您至少有一个线程修改它,并且至少有一个线程同时以任何方式触摸它,您可能最终会出现意外/未定义的行为。
此外,可变结构是代码味道。是否有某些特殊原因需要它成为struct
而不是class
?您是否需要此数据的值类型语义?
答案 3 :(得分:0)
不同线程对可变结构的不同成员的直接读写不会相互干扰。不同线程通过Interlocked方法对同一成员的访问将根据这些方法的语义来表现。这些事实可能允许可变结构允许线程安全行为。
保持结构的可变存储位置除了直接替换之外不提供任何突变方式,不提供任何线程安全性,除非在结构保存单个32位整数或单个对象引用的情况下,尝试读取这样的(单项)结构存储位置在写入的同时保证读取完全旧数据或全新数据。请注意,不可能将任何Interlocked方法与不可变结构一起使用 - 甚至是只包含单个整数或对象引用的结构。
答案 4 :(得分:0)
不,他们不是。 我创建了非常简单的应用程序,以查看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());
}
}
}
答案 5 :(得分:-2)
没有。为什么它是线程安全的?这只是数据。它不会被魔法变成线程安全的。