views:

85

answers:

2

I have a class that represents the "state of the world". The class has many collections of many other objects which in turn have references to more objects and collections of objects, sometimes even references to their ancestors in the "world hierarchy". To simplify what's being said, here is an example (transformed into XML, details omitted):

<World>
<Country>
 <City>
  <Block>
  ...
  </Block>
 </City>
 <City>
  <Block>
  ...
  </Block>
 </City>
</Country>
...
<Country>
 <City>
  <Block> ... </Block>
  ...
  <Block> ... </Block>
 </City>
 <City>
  <Block> ... </Block>
 </City>
</Country>
</World>

There are two threads, the UI thread and the background thread (which is actually a server).

The server receives the messages which modify the "state of the world" (adding cities, blocks etc.).

The UI thread draws the world's state to screen using PictureBox object every once in a while. The presentation layer has only reference to the IWorld object (which World implements), and it has no way to access the elements within.

The UI thread should lock the complete state of the world so the world can't be changed (by the server) during the drawing (that would make an inconsistent world picture). Since it has only reference to the IWorld object, this is the only thing to be locked.

My question is whether this lock would suffice (that is, lock all fields and properties this object has, recursively), or each object should be locked separately. What is the right approach to this problem?

Note: UI has no way to contact server (meaning it can't tell server to stop changing the world, and then tell it to resume after rendering).

EDIT: If the World and all classes in hierachy implement ILock interface which provides Lock() method which would call Lock() on all lower levels (recursively), this would probably be prone to deadlock (cyclic references) or simply be too expensive.

I guess design change is in order.

+1  A: 

The main question is how you access the elements below the world. If everything is accessed via a "World singleton", then a global lock is fine and safe.
If you access the countries, cities, etc... directly, then you need individual locks which need to be requested in the exact same order if you need to change several parts of the chain the same time.

weismat
So if the server can access the lower-level world objects via IWorldModify interface, then locking only the top level object would not be enough?
kornelijepetak
Look at the MS implementation of a Singleton:http://msdn.microsoft.com/en-us/library/ms998558.aspxIf you have used the multi-threaded implementation, then you are fine. Jon's suggestions are valid as well if you run into performance problems.
weismat
+3  A: 

You don't need to lock recursively - if both threads just lock on the same IWorld reference, that will be enough to assure mutual exclusivity.

Locking on the individual elements would give you finer grained locking of course, but then you could end up with an inconsistent picture - I would imagine that you'd want to see a whole set of changes from the server at a time.

However, a better model would usually be to make the server publish "snapshots" and then create a new copy of the world and modify the copy, turning it into a snapshot when appropriate. That way you don't need this mutual exclusion which could block your UI when it wants to draw the world while the server is changing it.

Jon Skeet
Even though this does not explicitely answer the question, it provides a good point on design change needed (and pretty easy to implement). Thanks.
kornelijepetak
I don't see in what way it doesn't answer the question: the first sentence answers it, I thought. The easiest way to assure mutual exclusion for the whole world is just to make both threads lock on the same reference.
Jon Skeet
In a way it does, in a way it doesn't :)It is obvious that if two objects try to lock the same reference it is enough to assure mutual exclusivity. But my question was actually whether it's enough if one objects locks the top-level objects and another object locks the lower-level object. But now that I think about it, it would have no sense that way. The OS would have to lock all references to objects and that would create a deadlock at first cyclic reference found. So I guess, my question was not exactly thought thru in the first place.
kornelijepetak