views:

173

answers:

6

I've got a class with several properties. On every value update, a Store method is called which stores all fields (in a file).

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);
        }
    }
}

Is this design thread-safe?

(Btw, if you can think of a more appropriate caption, feel free to edit.)

I think it is thread safe. If properties are changed on multiple threads, the values will be set in random order, atomic Stores will happen in a random order, but in the end, every property will have its latest value, and at the very end, an atomic Store happens, ensuring that the file is up-to-date.

Clarification: The properties will not be set very often, but they may be set at the same time. What matters is having a valid file most of the time.

If a thread is going to change a property with regards to a property value, it has to lock on the whole object to sync with other threads. That's basically the same as with locking on a List on enumeration and is not a responsibility of this class.

+5  A: 

It depends what you're calling on different threads.

If you set properties on different threads at once, it isn't thread-safe, because the properties can change while they're being serialized.

SLaks
Yes, but in that case they will be serialized again after they changed.
mafutrct
A: 

I would suggest using some sort of Mutex to ensure the data is only serialized at a particular point, it could do it just as the update changes or is about to change.

The following link takes you to a simple example on the MSDN website that should hopefully demonstrate it for you:

Mutex Example

Jamie Keeling
How would I do that? I'm not familiar with Mutex, could you provide a short example using the OP code? Also, I feel this is overkill iff it is not actually needed.
mafutrct
I've added a link to some sample code
Jamie Keeling
+1  A: 

No.

In the case you want to synchronize the properties theirself, this code is not thread safe, since the 'lock' is not on the _Prop1 value, but only on _Ser. Indeed when a thread is gettin the property, the property could be set by another thread.

Even the serialization process, _Ser access to properties which can be changed by other threads in execution (while _Ser is running, another thread set Prop1).

This code actually disallow the use of the XmlSerialize _Ser object by multiple threads. If it is what you want to get...


The answer depends essentially on what you want to get.

Luca
Setting a property's backend value is always atomic. If different threads access the same property, they have to sync their access (just like when enumerating something) - it is impossible to do that in this class.
mafutrct
Even after properties have been set by multiple threads, they will eventually be stored, so I'm not sure why you think using XmlSerializer prevents that. Did you get you wrong?
mafutrct
Ah, I think I get it now. You mean that the file may be in an incomplete state for a short time, until it is written again with the latest values, right? That's true. In my specific case, it does not matter, but I believe this is actually an issue of this code in the general case.
mafutrct
I mean that the _Ser could read a different _Prop1 value while serializing because another thread could set the _Prop1 value, since it isn't synchronized using lock. If the serialization restart because the latter thread has set Prop1, it is another story.
Luca
A: 

If the Prop1 property can be called from multiple threads, make the _Prop1 field volatile.

private volatile int _Prop1;

From MSDN,

The volatile keyword indicates that a field might be modified by multiple threads that are executing at the same time. Fields that are declared volatile are not subject to compiler optimizations that assume access by a single thread. This ensures that the most up-to-date value is present in the field at all times.

Consider it good practice. It's not going to make the code you posted thread safe because, as others have said, while the class is being serialized, the value of _Prop1 can be changed by another thread. Still, if you have a field that is capable of being read and written by multiple threads, marking the field as volatile guarantees that your code sees the most current value.

Matt Davis
I don't understand how this helps in any way?
mafutrct
(after edit) I'm sorry, I still don't get it. I doubt the compiler could optimize access to a public int property in a way that affects thread safety.
mafutrct
@mafutrct: It helps in that it forces a reading thread to read the latest value... or at least something like that. Volatility is actually rather more subtle than that. Lock free threading with mutable types is a nasty can of worms :(
Jon Skeet
I should point out that volatile doesn't just affect the C# compiler. It also affects the code generated by the JIT, ensuring that the memory model works appropriately.
Jon Skeet
+1  A: 

There's not enough code to make the call. But sure, nothing good is going to happen if you don't serialize write access to the file. The 2nd thread that assigns the property is going to bomb on an IOException if the 1st thread is still busy writing the file.

Fine-grained locking like this is usually a problem. The client code could be busy changing more than one property of the class. You'll get a partial update if an exception is raised, producing a file that contains serialized state that is invalid and may cause trouble when it is read. You'll need something like a BeginUpdate(), EndUpdate() pair.

Hans Passant
File access is synced in the OP code, isn't it?
mafutrct
About your second paragraph: That's true, but it is intrinsically not possible to account for this issue in the class itself - unless Store is deferred until all values have been changed. I'm not sure how to implement that. Also, in my case, it's not required :)
mafutrct
@mafutrct: yes, you can rarely make a class thread-safe yourself without help from the client code. That's why the .NET framework classes leave it entirely up to the client code. Needless locking when the client just never uses it in multiple threads is not great either. Although it doesn't exactly matter here, the property setter is already very slow. Look at the .NET ApplicationSettings class for guidance, it has an explicit Save() method. But it does keep track of a "ought to save" state.
Hans Passant
The "ought to save"-state idea is nice. Seems like a valid solution for problems like this.
mafutrct
A: 

Not thread safe, unless the property type is atomic.

Simple example, with threads A and B.

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

The integrity of the data is possibly compromised. Even with types as simple as Int64, problems can occur in theory.

Putting another lock(_Ser) within the setter would help.

dbkk
The property type is atomic.I'd like to avoid `lock(_Ser)` iff possible.In your example, I think that's exactly what should happen - the value written in the end is the latest value.
mafutrct
Atomic != volatile. Just because it's being stored atomically doesn't mean that another thread will read any new value being stored.
Jon Skeet