Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/loops/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C#并发字典对象读取/更新线程安全问题_C#_.net Core_Concurrentdictionary - Fatal编程技术网

C#并发字典对象读取/更新线程安全问题

C#并发字典对象读取/更新线程安全问题,c#,.net-core,concurrentdictionary,C#,.net Core,Concurrentdictionary,在下面的代码中,我有一个并发字典,用于存储单个键/值对,其中值是字符串的集合 我将从不同的线程读取和更新这个单一键/值对中的字符串 我知道,如果一个线程在另一个线程可能完成读取之前更改了一个值,那么并发字典并不是完全线程安全的。但同样地,我也不确定字符串值是否真的会出现在这个话题中,是否有人能提出建议 还值得一提的是,尽管我将这个“GetRunTimeVariables”方法放入了一个依赖注入接口,但实际上我不能一直使用DI来访问这个方法,因为在应用程序启动和OIDC事件登录/注销的阶段,我需要

在下面的代码中,我有一个并发字典,用于存储单个键/值对,其中值是字符串的集合

我将从不同的线程读取和更新这个单一键/值对中的字符串

我知道,如果一个线程在另一个线程可能完成读取之前更改了一个值,那么并发字典并不是完全线程安全的。但同样地,我也不确定字符串值是否真的会出现在这个话题中,是否有人能提出建议

还值得一提的是,尽管我将这个“GetRunTimeVariables”方法放入了一个依赖注入接口,但实际上我不能一直使用DI来访问这个方法,因为在应用程序启动和OIDC事件登录/注销的阶段,我需要访问不能使用依赖注入的类中的字典值,因此,本质上,我可以在应用程序的整个生命周期中,根据需要从nay-means访问此词典

最后,我不确定将这个方法推广到一个接口中是否有任何好处,另一个选项只是在每次我需要它的时候更新对这个类的引用,对此的一些想法将不胜感激

public class RunTimeSettings : IRunTimeSettings
{
    // Create a new static instance of the RunTimeSettingsDictionary that is used for storing settings that are used for quick
    // access during the life time of the application. Various other classes/threads will read/update the parameters.
    public static readonly ConcurrentDictionary<int, RunTimeVariables> RunTimeSettingsDictionary = new ConcurrentDictionary<int, RunTimeVariables>();

    public object GetRunTimeVariables()
    {
        dynamic settings = new RunTimeVariables();

        if (RunTimeSettingsDictionary.TryGetValue(1, out RunTimeVariables runTimeVariables))
        {
            settings.Sitename = runTimeVariables.SiteName;                
            settings.Street = runTimeVariables.Street;
            settings.City = runTimeVariables.City;
            settings.Country = runTimeVariables.Country;
            settings.PostCode = runTimeVariables.PostCode;
            settings.Latitude = runTimeVariables.Latitude;
            settings.Longitude = runTimeVariables.Longitude;
        }

        return settings;
    }
}

System.String
类型(C#的经典
String
s)是不可变的。任何人都不能修改
字符串的“内容”。任何人都可以将属性引用设置为不同的
字符串


但事实上,这里唯一的问题是各种值可能会被取消同步。您有各种相关的属性。如果一个线程正在修改对象,而另一个线程正在读取它,那么读取线程可能会看到“旧”版本的一些属性和“新”版本的一些属性。如果对象一旦写入
ConcurrentDictionary
后没有更改(至少作为业务规则是“不可变的”),那么这不是一个大问题。显然,正确的解决方案是将
运行时变量
设置为不可变对象(例如,仅由在实例化时初始化的
只读
字段组成)

系统。字符串
类型(C#的经典
字符串
s)是不可变的。任何人都不能修改
字符串的“内容”。任何人都可以将属性引用设置为不同的
字符串


但事实上,这里唯一的问题是各种值可能会被取消同步。您有各种相关的属性。如果一个线程正在修改对象,而另一个线程正在读取它,那么读取线程可能会看到“旧”版本的一些属性和“新”版本的一些属性。如果对象一旦写入
ConcurrentDictionary
后没有更改(至少作为业务规则是“不可变的”),那么这不是一个大问题。显然,正确的解决方案是将
RuntimeVariables
设置为不可变对象(例如,仅由在实例化时初始化的
readonly
字段组成)

如果我没有弄错的话
TryGetValue
不是线程安全的,但它需要更多的研究()有人会认为“尝试做”某件事肯定比说“做某件事”更安全,嗯,我有兴趣了解更多这方面的信息。试图理解官方MS文档很少helps@ipinak ??? 所有这些都是线程安全的。唯一的问题是,如果尝试执行
TryGetValue
,失败时执行显式
Add
TryGetValue
的结果仅在检索时有效,不能保证它在检索前一刻或之后一刻有效。如果我没有弄错的话,
TryGetValue
不是线程安全的,但它需要更多的调查()有人会认为“尝试做”某件事肯定比说“做某件事”更安全,嗯,我有兴趣了解更多这方面的信息。试图理解官方MS文档很少helps@ipinak ??? 所有这些都是线程安全的。唯一的问题是,如果尝试执行
TryGetValue
,失败时执行显式
Add
TryGetValue
的结果仅在检索时有效,不能保证它在检索前一刻或后一刻有效。您好,我不理解您解释的后半部分,但值得一提的是,此字符串值集合仅用于日志记录,因此,如果一个线程与另一个线程之间的值存在瞬时的不一致性,则只会影响1qty日志事务。你认为使用我的界面是没有意义的吗?实际上只需要更少的代码行就可以在需要时更新类。。。thx@OJB1将其放在接口中可以更容易地模拟它进行单元测试,至少对于可单元测试的类,字典的值由单元测试设置很重要,和/或无法从正常来源加载字典的值(可能是因为单元测试无法访问db/到具有这些值的远程服务器)@obj1解释的哪一部分不清楚?从哪个词到哪个词?你的最后一句话,你能举个例子说明我应该做的更正吗?thx@OJB1在C#9.0中,将有一个新特性。如果没有C#9.0,您将使用builder模式,使用可变类(如
StringBuilder
),该类具有创建不可变对象的方法(返回
字符串的
.ToString()
)。对于不可变对象,在您读取对象时,另一个线程不可能更改该对象。如果它想更改对象,它必须创建一个具有新值的新对象,然后在dict中切换对象。嗨,我不理解你解释的后面部分,但值得一提的是,这个字符串值集合只是用于日志记录,所以如果va中有一个瞬间的不一致性
public class RunTimeVariables
{
    public bool InstalledLocationConfigured { get; set; }
    public string SiteName { get; set; }
    public string Street { get; set; }
    public string City { get; set; }
    public string Country { get; set; }
    public string PostCode { get; set; }
    public string Latitude { get; set; }
    public string Longitude { get; set; }
}