代码之家  ›  专栏  ›  技术社区  ›  Kaveh Shahbazian

C结构是线程安全的吗?

  •  8
  • Kaveh Shahbazian  · 技术社区  · 16 年前

    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,线程安全吗?

    6 回复  |  直到 11 年前
        1
  •  8
  •   Community Mohan Dere    8 年前

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

    然而,按值复制的语义认为,结构与这种转换有很大的相关性。

    如果要传递结构并以某种方式将其分配给变量或传递值参数(没有ref或out关键字),则 复制 正在被使用。

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

    如果您以不涉及按值复制语义的方式直接访问结构(例如,访问结构类型的静态字段,以及 Marc Gravel points out in his answer ,还有许多其他方法)跨多个线程,然后必须考虑实例的线程安全性。

        2
  •  9
  •   Marc Gravell    16 年前

    好吧-最佳实践是结构应该始终是不可变的(除了在一些非常具体的场景中,甚至在有风险的情况下)。不可变数据总是线程安全的。因此,如果您遵循最佳实践并做到了这一点:

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

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

    还要注意原子性规则适用,因此即使是单个读取或更新 DadData.TheData 不能假定为线程安全,甚至 具有 不可变的结构。您可以(尤其是对于超大结构)让一个线程读取该结构,而另一个线程重新写入该结构;如果不同步,则会发生坏事情(最终)。

        3
  •  1
  •   Aaronaught    16 年前

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

    此外,可变结构是代码气味。你有什么特别的理由需要它成为 结构 而不是 class ?您需要这个数据的值类型语义吗?

        4
  •  0
  •   supercat    11 年前

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

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

        5
  •  0
  •   Teoman shipahi    11 年前

    不,他们不是。 我创建了一个非常简单的应用程序来查看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());
    
            }
        }
    }
    
        6
  •  -2
  •   John Saunders    16 年前

    不,为什么它是线程安全的?只是数据而已。它不会因为魔法而变得安全。