Click to See Complete Forum and Search --> : Threading and deadlocks.


billw
February 17th, 2007, 07:29 PM
I am trying to run a number of threads simultaneously, and running into what appears to be deadlock problems (if I understand the term correctly):
thread A locks resource A
thread B locks resource B
thread A trys to lock resource B but cannot so waits here
thread B trys to lock resource A but cannot so waits here
What design considerations should I be making to avoid these problems?
The data used in the threads is a shared scenegraph in a 3D application I am writing, how should I go about making these classes thread safe?
It seems to me that as soon as a call is made to a function within a locked area of code there is the possiblity of deadlocks (as you don't know what is being locked within the functions code).
e.g.

public void someFunc()
{
lock(this)
{
someOtherFunc(); // <--- not thread safe?
}
}

Also as soon as you allow a reference to a field member of an class to pass outside the code of the object itself it becomes thread unsafe, as the client is then responsible for locking the data.
e.g.

private List<int> numbers;
public List<int> Numbers
{
get{ lock(this) return numbers; } // <--- thread unsafe even with a lock, because "numbers" is a reference?
}

That means either access to objects stored within other objects must be done through an intemediary threadsafe interface, or the objects themselves must be known to be threadsafe.
The ammount of different ways that an object, its code and its data can become thread unsafe is making my head spin!
Are there any foolproof guide lines I can follow to write thread safe code? Like a best practices but for threads.
Thanks!

TheCPUWizard
February 17th, 2007, 08:27 PM
Simply avoid the "Deadly Embrace".

Have both A and B attempt to get a lock on "X" at the very beginning.

billw
February 17th, 2007, 08:53 PM
If I am calling external code then how do I know what I need to lock for it?
Also if both threads try and lock both resources then they won't run concurrently. I am only locking bits of code where the data is being used, and leaving other bits of code unlocked to allow the threads to run at the same time.
e.g.

void threadAFunc()
{
while(!done)
{
lock(A) A.someFunc();
lock(B) B.someFunc();
}
}

void threadBFunc()
{
while(!done)
{
lock(A) A.someOtherFunc();
lock(B) B.someOtherFunc();
}
}

The problem occurs when, for instance, A.someFunc() locks B and B.someOtherFunc() locks A.

MadHatter
February 17th, 2007, 09:16 PM
do not lock the entire instance... create synchronizing objects and lock each inside the various methods (like one lock object per method or whatever), or use a mutex.

TheCPUWizard
February 17th, 2007, 11:07 PM
As mad hatter says, in your code, the locks should be localozed. However if you are calling external code (not under your control), then a less granular approach may be required.

The whole point is to NEVER have a condition where there can be conflicting locks, OR be prepared to handle release and retry mechanisms. In my experience 95% of the cases can be handled with a clean lock hierarchy, and only 5% require dealing with rollback and retry.

billw
February 18th, 2007, 12:14 AM
Thanks for the tips.
Any advice on creating a clean lock hierarchy?
It occured to me that I could write a lock manager that I register the different locks required with, and give them an order (hierarchy), then I request locks through the manager, and it will lock the object as well as all objects below it in the hierarchy, in the same order every time.
This seems like it could work to me, but the overhead might be quite painful.
The idea of trying to work out the lock hierarchy for every object within itself and relative to other objects and then maintaining it as changes are made seems rather daunting so I am looking for an easy way out (unless there isn't one ofcourse in which case I will buckle down to it).

MadHatter
February 18th, 2007, 12:24 AM
use a mutex.

TheCPUWizard
February 18th, 2007, 12:56 AM
use a mutex.


Not really useful advice, unless you are counting on the semantic defination of "a".

billw
February 18th, 2007, 01:35 AM
After looking at the msdn info on the Mutex class, and on google I still can't see any real difference between a mutex and a standard lock (Monitor), except that the Mutex class can wait for a signal from multiple WaitHandles. I can't see how this applies to the problem of making lock hierarchies easy to manage and maintain.

TheCPUWizard
February 18th, 2007, 01:39 AM
bill. That is why I made my previous post. If MadHatter was talking about using a single mutex to wrap all of the locks, then it would work (but it does not matter what type of locking mechanism is actually used). If he was talking about using mutile distinct mutexes, then the same problem that you have would still exist.