11

我在 C# 多线程程序中遇到了不良行为。我的一些静态成员在其他线程中丢失了它们的值,而相同声明类型的一些静态值不会丢失它们的值。

public class Context {
  public Int32 ID { get; set; }
  public String Name { get; set; }

  public Context(Int32 NewID, String NewName){
      this.ID = NewID;
      this.Name = NewName;
  }
}

public class Root {
    public static Context MyContext;
    public static Processor MyProcessor;

   public Root(){
     Root.MyContext = new Context(1,"Hal");

     if(Root.MyContext.ID == null || Root.MyContext.ID != 1){
         throw new Exception("Its bogus!") // Never gets thrown
     }

     if(Root.MyContext.Name == null || Root.MyContext.Name != "Hal"){
         throw new Exception("It's VERY Bogus!"); // Never gets thrown
     } 

     Root.MyProcessor = new MyProcessor();
     Root.MyProcessor.Start();
   }
}

public class Processor {
   public Processor() {
   }

   public void Start(){
      Thread T= new Thread (()=> {

          if(Root.MyContext.Name == null || Root.MyContext.Name != "Hal"){
                throw new Exception("Ive lost my value!"); // Never gets Thrown
          }

          if(Root.MyContext.ID == null){
              throw new Exception("Ive lost my value!"); // Always gets thrown
          }

      });
   }
}

这是使用某些类型的静态成员时的线程突变问题吗?

4

3 回答 3

13

使用volatile或使用 Interlocked 访问变量。

您遇到的问题是编译器(以及本机编译器)可以自由地优化对变量的访问,因为他认为没有它们是合适的。所以他可能会将一个变量转储到一个寄存器中,而不是重新读取它。

为了避免它,您必须确保变量实际上已被真正读取。挥发性做到这一点。互锁也可以做到这一点(并允许增量/添加等以原子方式发生)。

你必须决定哪个更好。两者都将内存屏障强加到处理器上,这在经常执行时确实具有不小的成本。我经常使用的一种模式是让这些对象大部分只读,这样我就只替换根对象(一个内存屏障)。手动处理内存障碍(可能,阅读关键字手册)是一种痛苦。但是,它的效率要高得多 - 取决于你在那里做了多少等等。它可能是需要的。

于 2012-12-18T19:30:42.333 回答
3

尝试volatile对从多个线程修改/访问的变量使用修饰符

于 2012-12-18T19:26:20.253 回答
2

多线程应用程序中的共享变量值是不确定的!您应该对每个共享资源使用锁定以避免逻辑冲突:

    static Readonly Object _lock=new Object();
    lock(_lock)
    {
    //accessing your shared variable
    }
于 2012-12-18T19:42:32.397 回答