共享变量上的C#多线程冲突

共享变量上的C#多线程冲突,c#,multithreading,thread-safety,C#,Multithreading,Thread Safety,我的问题如下: 我有一个GUI在它自己的线程1中运行,设置了一些配置设置 配置本身将由不同类中的另一个线程2使用(仅读访问),但当我在GUI中应用时,它应该尽快更新 该配置包含bool和dictionary等变量 实现这个问题的安全方法是什么 到目前为止,我的想法是: 在某个静态实例变量中包含当前配置的类: public class Config { static Config Instance; int a; Dictionary<int, int> b;

我的问题如下: 我有一个GUI在它自己的线程1中运行,设置了一些配置设置

配置本身将由不同类中的另一个线程2使用(仅读访问),但当我在GUI中应用时,它应该尽快更新

该配置包含bool和dictionary等变量

实现这个问题的安全方法是什么

到目前为止,我的想法是: 在某个静态实例变量中包含当前配置的类:

public class Config
{
   static Config Instance;
   int a;
   Dictionary<int, int> b;
   ...
   public void update(Config newConfig) {}
}
公共类配置
{
静态配置实例;
INTA;
词典b;
...
公共无效更新(Config newConfig){}
}
因此线程2中的所有类都可以访问最新的设置

当我按下GUI中的“应用”按钮应用最新设置时,就会调用更新函数


但是线程安全呢?如何仅在thread2未读取时设置变量,并在设置变量时使thread2等待?我没有在c#中使用多线程的任何经验,并且我知道在某些线程使用时可能必须锁定变量。或者有其他聪明的方法来实现它吗?

您正在寻找单例设计模式:

public sealed class Config
{
    private static Config instance = null;
    private static readonly object padlock = new object();

    #region ConfigProperties
    //put public properties here to represent config values
    public int a { get; set; }
    public Dictionary<int, int> b { get; set; }
    #endregion ConfigProperties

    Config()
    {
        ReadConfig();
    }

    public static Config Instance
    {
        get
        {
            lock (padlock)
            {
                if (instance == null)
                {
                    instance = new Config();
                }
                return instance;
            }
        }
    }
    public void ReadConfig()
    {
        //read from your config file and set properties
    }
    public void SaveConfig()
    {
        //write properties back into your config file
    }
}
公共密封类配置
{
私有静态配置实例=null;
私有静态只读对象挂锁=新对象();
#区域配置属性
//在此处放置公共属性以表示配置值
公共int a{get;set;}
公共字典b{get;set;}
#endregion配置属性
Config()
{
ReadConfig();
}
公共静态配置实例
{
收到
{
锁(挂锁)
{
if(实例==null)
{
instance=new Config();
}
返回实例;
}
}
}
public void ReadConfig()
{
//读取配置文件并设置属性
}
public void SaveConfig()
{
//将属性写回配置文件
}
}
此类是线程安全的,这意味着您可以使用
Config.Instance.
后跟任何配置属性,并且可以安全地访问它们,而不会导致跨线程问题


有更优雅的解决方案和更详细的解释。

如果您有一个
Config
的共享实例,并且所有东西都使用该实例,那么在您读取它们时,其他线程可能会更改属性。为了防止出现这种情况,您可以
锁定对单个属性的访问。但这不是一个愉快的想法。相反,最好尽量减少你不得不担心的情况

一个步骤是让其他线程创建
Config
的新实例,而不是所有线程共享。您的GUI(表单?)可以接收全新的
Config
实例

如果您有这样的问题(例如,属性是否应该是公共的,或者您的类如何接收
Config
的实例是一个完全不同的问题)

如果通过提供具有不同值的
Config
的不同实例来设置该属性,则这是一个原子操作。您不必担心您的类会在更新引用的中途访问MyConfigInstance

这可能就是你所需要的。但仍有出错的余地。现在,您的类引用了
Config
的一个新实例。但是,还有哪些线程可以访问相同的引用,并且仍然可以更改它呢?至少将其传递给该类的线程仍然具有访问权限。如果该线程传递该实例,而现在其他类引用了该实例,该怎么办

这在您的应用程序中可能不是问题。也许您确切地知道哪些线程可以访问哪些类的实例。但是,如果事情变得更复杂,我们还是宁愿避开这个问题,也不愿试图跟踪它

为此,如果多个线程将共享一个类的实例,通常最好使该类不可变。是这样的:

public class MyImmutableClass
{
    public MyImmutableClass(string stringValue, int intValue)
    {
        StringValue = stringValue;
        IntValue = intValue;
    }

    public string StringValue { get; }
    public int IntValue { get; }
}
它的属性是在创建时设置的,不能更改。此时,您不必关心有多少线程引用了类的实例,因为它们都不能更改它。可以将对一个实例的引用替换为对新实例的引用,但这不会影响其他引用

不可变类的另一个好处是,如果值仅在构造函数中设置,则可以验证它们。与上面的示例一样,如果
stringValue
为null,则可以引发异常。这样,您总是知道类处于有效状态。不可能创建处于无效状态的实例


我们倾向于创建可变类,因为创建读/写属性比创建构造函数更快。但是,如果我们要将实例传递给不同的线程,那么有时最好从不变性开始,这迫使我们思考和控制多个线程可以或不能完成的事情

如果有疑问,请锁定它。如果你甚至在考虑是否锁定。。。只要锁上。读或写时,锁定共享状态。也就是说,有很多可能的方法来解决这个问题,而多线程/共享状态是一个相当深入的话题,所以我认为这个问题过于宽泛。
public class MyImmutableClass
{
    public MyImmutableClass(string stringValue, int intValue)
    {
        StringValue = stringValue;
        IntValue = intValue;
    }

    public string StringValue { get; }
    public int IntValue { get; }
}