views:

569

answers:

6

I have two threads referencing the same variable -- the UI thread and a timer thread. As such, I have wrapped access to it in lock statements in both threads. The timer thread's access has priority -- if it's using the variable and the UI thread also wants access, I want the UI thread's operation to complete, but only after the timer thread's operation completes.

However, the timer thread may delegate back to the UI thread, so the UI thread needs to be free to handle that. To accommodate that, I have the UI thread launching a third thread to handle its operation so that it (that third thread) can wait for the timer operation to complete and the UI thread can be available. The locking happens in that third thread.

What is the correct pattern I should be using for this sort of synchronization?

+6  A: 

The general recommendation is that whatever is happening in critical sections should be as simple as possible. In particular you should avoid nested locks. Nested locks potentially are the source of deadlocks.

As applied to what you are doing in your 'timer' thread you probably should separate the critical sections from processing. IOW in the timer thread just retrieve the data from the common variable(s) and then do the rest of the processing including interactions with UI thread outside the lock.

Adding a third thread to the mix will not make your life any easier

mfeingold
Thank you for the "KISS" reminder.
oakskc
+1  A: 

A general rule to follow is that you minimize the amount of time that you hold a lock, and you don't call code you don't own and control (such as an event, a virtual method, or a UI thread) while holding a lock.

So the timer should not call back into the UI while holding the lock. If it needs transactional access to the data under the lock (read, call UI, write), then it should probably be designed to roll back and/or retry.

Barry Kelly
+2  A: 

The rule of thumb is to use the lightest lock for the shortest time possible.

There is quite a good article here: http://www.moserware.com/2008/09/how-do-locks-lock.html

Shiraz Bhaiji
A: 

Like mfeingold said, avoid it where possible. Where you cannot avoid it take care of taking the locks in both threads in the same order! I mean, if you have three locks A, B and C, if you have following lock patterns

  • Thread 1: A->B->C
  • Thread 2: A->C->B

then a deadlock is possible...

Nesting locks usually costs performance as you have to lock the outer locks even if you would sometimes not need to to ensure correct behavior in the case where the method is called from another thread.

jdehaan
A: 

Why are you doing this? It sounds complicated and error prone.

What is the timer thread delegating to the UI thread for?

It may be the case that it's necessary, but my immediate reaction is that you probably need to take a step back and consider whether your design is unnecessarily forcing you into complex synchronization issues.

Phil
You may be right, and I'm never against a re-evaluation.The timer thread delegates back to the UI thread in two scenarios:1. A call is made to an object that outputs to UI that was instantiated from the UI thread. Not all instances of this object make use of such UI. (I'd be happy to come away with a good pattern for this.)2. A call is made to a third-party object that must be called from the thread it was created on (in this case, the UI thread).
oakskc
Do the UI operations need to happen synchronously (i.e. do you need to wait until they complete?) - if not, you could consider making the calls to the UI thread asynchronously. This might then allow you to avoid the potential for deadlock
Phil
A: 

Please, Please, could you show me the code that you are using. I am trying to figure out how to do EXACTLY what you are doing and do not know how to do the thread locks and synchronization.

Thanks!

Mona Everett
I am a bit of a newbie to synchronization and don't assume to have the most correct of answers, but I do a lock on the object in question (must be a reference type, not a value type) by:lock(obj) {}...which prevents concurrent use of that object while the thread is within that block. I do the same thing in the other threads that want to make use of the same object. Generally, I do it during a write to the object so that a thread that reads from the object doesn't get invalid data and the write is done as a single atomic operation.
oakskc