代码之家  ›  专栏  ›  技术社区  ›  mafu

这个代码是线程安全的吗?

  •  1
  • mafu  · 技术社区  · 16 年前

    我有一个有几个性质的班。每次更新值时, 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 在枚举上,而不是此类的责任。

    6 回复  |  直到 16 年前
        1
  •  1
  •   Hans Passant    16 年前

    没有足够的代码来拨打电话。但是,如果不序列化对文件的写访问权,则不会发生什么好事。如果第一个线程仍在忙于写入文件,则分配该属性的第二个线程将在IOException上爆炸。

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

        2
  •  5
  •   SLaks    16 年前

    这取决于你在不同的线程上调用什么。

    如果一次在不同的线程上设置属性,这是不安全的,因为属性在序列化时会发生更改。

        3
  •  1
  •   Luca    16 年前

    不。

    在您希望同步属性本身的情况下,此代码不是线程安全的,因为“lock”不在“prop1”值上,而只在“ser”上。实际上,当线程获取属性时,该属性可以由另一个线程设置。

    即使在序列化过程中,用户也可以访问可由其他线程在执行过程中更改的属性(当用户运行时,另一个线程集prop1)。

    此代码实际上不允许多个线程使用xmlserialize-ser对象。 如果这是你想要的…


    答案基本上取决于你想要得到什么。

        4
  •  0
  •   Matt Davis    16 年前

    如果 Prop1 属性可以从多个线程调用,使 _Prop1 领域 volatile

    private volatile int _Prop1;
    

    来自msdn,

    这个 不稳定的 关键字表示 字段可能被多个 同时执行的线程 时间。声明的字段 不稳定的 不受编译器约束 假设访问由 单线程。这确保了 最新值出现在 在任何时候。

    认为这是一种良好的做法。它不会使您发布的代码安全,因为正如其他人所说,当类被序列化时, γ-脯氨酸 可以被其他线程更改。但是,如果您有一个可以由多个线程读写的字段,那么将该字段标记为volatile可以保证代码看到最新的值。

        5
  •  0
  •   dbkk    16 年前

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

    简单的例子,使用线程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) 在二传手的帮助下。

        6
  •  0
  •   Jamie Keeling    16 年前

    我建议使用某种互斥体来确保数据只在特定的点上序列化,它可以在更新更改或即将更改时进行序列化。

    下面的链接将带您到msdn网站上的一个简单示例,希望能为您演示它:

    Mutex Example