C#是否自己更新实例线程安全?

时间:2017-09-07 23:15:19

标签: c# multithreading thread-safety

我有一个名为Configuration的类,它有几个属性,将使用从数据库加载的值进行更新 我在OnDataChanged构造函数中有一个数据触发事件Configuration,其中eventhandler订阅了一个私有方法。
多个线程使用相同的Configuration实例。

如果在数据库表中更新了某些内容,则会激活OnDataChanged事件并调用私有方法,并在此私有方法中使用数据库中的最新数据更新Configuration属性,以便已经使用该实例的线程将具有更新的数据。

这是一个线程安全吗? 如果不是,我们怎样才能使这个线程安全?我没有使用任何锁。

编辑:

添加示例代码:

public class Configuration
{
    private GeneralConfiguration _generalConfiguration;
    private AccountConfiguration _accountConfiguration;
    private readonly SqlTableDependency<Model> _dependency;
    public Configuration()
    {
        _dependency = new SqlTableDependency<Model>("connectionstring", "dbo.Configuration");
        _dependency.OnChanged += _dependency_OnChanged;
        _dependency.Start();
    }

    private void _dependency_OnChanged(object sender, RecordChangedEventArgs<Model> e)
    {
        Init();
    }

    private Configuration Init()
    {
        DataAccess da = new DataAccess();
        List<string> configs = da.GetConfigData();
        _generalConfiguration = JsonConvert.Deserialize<GeneralConfiguration>(configs[0]);
        _accountConfiguration = JsonConvert.Deserialize<AccountConfiguration>(configs[1]);
        return this;
    }

    public GeneralConfiguration GeneralConfiguration { get { return _generalConfiguration; } }

    public AccountConfiguration AccountConfiguration { get { return _accountConfiguration; } }
}

1 个答案:

答案 0 :(得分:0)

正如其他人所评论的那样,您的代码可能不是线程安全的。使线程安全的最简单方法是创建一个私有对象并使用lock关键字 - 假设您没有使用任何async代码。

public class Configuration {
   private object sync = new object();
   private int someSetting1;
   public int SomeSetting1 {
      get {
         lock (sync) {
            return someSetting1;
         }
      }
   }

   private decimal someSetting2;
   public decimal SomeSetting2 {
      get {
         lock (sync) {
            return someSetting2;
         }
      }
   }

   private void OnDataChanged() {
      lock (sync) {
         someSetting1 = loadFromDatabase();
         someSetting2 = loadFromDatabase();
      }
   }
}

如果您有多个需要一起更改的设置,那么这很难看。如果someSetting1someSetting2的值彼此互补(例如用户名和密码对),则您有一个竞争条件,其中OnDataChanged可以在两次调用之间调用SomeSetting1SomeSetting2,导致您的调用代码变得不协调。如果这是一个问题,那么您需要将锁定移到Configuration类之外。

一种方法就是每次访问时锁定配置单例对象 - 无论是更新它还是从中读取一组值。避免lock完全依赖于引用的原子性的另一种方法是让您的Configuration类在子类中存储一组值并向该类添加公共访问器。 OnDataChanged将换出当前值类,并且调用代码将获取当前值,然后从该不可变实例获取他们想要的所有值:

public class Configuration {
   public class Values {  
      public int SomeSetting1 { get; }
      public int SomeSetting2 { get; }
   }
   private Values currentValues;
   public Values CurrentValues {
      get {
         return currentValues;
      }
   }
   private void OnDataChanged() {
      Values newValues = new Values(getValuesFromDatabase());
      currentValues = newValues;
   }
}

对于值集合,调用代码将执行类似的操作:

var values = configuration.CurrentValues;
doSomething(values.SomeSetting1, values.SomeSetting2);