C#结构是否安全?

时间:2010-02-28 22:48:25

标签: c# concurrency struct thread-safety parallel-processing

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

6 个答案:

答案 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)

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

相关问题