繁体   English   中英

此代码是线程安全的吗?

[英]Is this code thread-safe?

我有一堂课,有几个属性。 每次值更新时,都会调用一个Store方法,该方法将所有字段存储在一个文件中。

private int _Prop1;
public int Prop1 {
    get {
        return _Prop1;
    }
    set {
        _Prop1 = value;
        Store();
    }
}

// more similar properties here...

private XmlSerializer _Ser = new ...;
private void Store()
{
    lock (_Ser) {
        using (FileStream fs = new ...) {
            _Ser.Serialize (fs, this);
        }
    }
}

这个设计是线程安全的吗?

(顺便说一句,如果您能想到一个更合适的标题,请随时进行编辑。)

我认为这是线程安全的。 如果在多个线程上更改属性,则将以随机顺序设置值,原子存储将以随机顺序发生,但是最后,每个属性将具有其最新值,最后,原子存储发生,确保文件是最新的。

说明:这些属性不会经常设置,但可以同时设置。 重要的是大多数时候都有一个有效的文件。

如果线程要更改有关属性值的属性,则它必须锁定整个对象才能与其他线程同步。 基本上与锁定枚举时的List相同,这不是此类的责任。

这取决于您在不同线程上调用的内容。

如果您一次在不同的线程上设置属性,则它不是线程安全的,因为属性在序列化时可能会更改。

没有。

如果您要自己同步属性,则此代码不是线程安全的,因为“锁”不在_Prop1值上,而仅在_Ser上。 实际上,当一个线程成为该属性时,该属性可以由另一个线程设置。

即使是序列化过程,_Ser也可以访问其他执行中的线程可以更改的属性(在_Ser运行时,另一个线程设置为Prop1)。

这段代码实际上禁止多个线程使用XmlSerialize _Ser对象。 如果这是您想要得到的...


答案基本上取决于您想要获得什么。

没有足够的代码来拨打电话。 但是可以肯定的是,如果不序列化对文件的写访问,将不会有任何好处。 如果第一个线程仍在忙于写入文件,则分配该属性的第二个线程将在IOException上轰炸。

像这样的细粒度锁定通常是一个问题。 客户端代码可能忙于更改类的多个属性。 如果引发异常,您将得到部分更新,生成的文件包含无效的序列化状态,并且在读取时可能会引起麻烦。 您将需要类似BeginUpdate(),EndUpdate()对的东西。

我建议使用某种互斥锁来确保仅在特定点对数据进行序列化,这可以在更新更改或即将更改时执行。

以下链接将您带到MSDN网站上的一个简单示例,希望可以为您演示该示例:

互斥体示例

如果可以从多个线程调用Prop1属性,则使_Prop1字段为volatile

private volatile int _Prop1;

从MSDN,

volatile关键字指示一个字段可能被同时执行的多个线程修改。 声明为易失性的字段不受假定单个线程访问的编译器优化的约束。 这样可以确保始终在字段中显示最新值。

认为这是一种好习惯。 这样做不会使您发布的代码的线程安全,因为,正如其他人所说的那样,当类被序列化时, _Prop1的值可以由另一个线程更改。 尽管如此,如果您拥有一个可以被多个线程读取和写入的字段,则将该字段标记为volatile可以确保您的代码看到最新的值。

除非属性类型是原子的,否则不是线程安全的。

简单示例,线程A和B。

A: Prop1 = foo
A: Store()
A:    ... store saves foo.Var1
B: Prop1 = bar
A:    ... store saves bar.Var2 (instead of foo.Var2)

数据的完整性可能会受到损害。 即使使用Int64这样简单的类型,理论上也会出现问题。

在设置器中放置另一个lock(_Ser)将有所帮助。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM