Click to See Complete Forum and Search --> : lock() is not working...
noods
February 25th, 2005, 03:20 PM
Hi there,
I have a class A which needs to add data to a collection in class B. A works off a timer and adds to B. B works off a timer and enumerates through its collection. I will have multiple B objects.
The code will basically look like this...
//CLASS A
...
b.AddData(abc);
...
//CLASS B
private ArrayList g_List ;
public bool AddData(ArrayList al)
{
lock(g_List.syncRoot) {
g_List.Add(al)
}
}
private OnTimer(...)
{
lock(g_list.syncRoot){
foreach(ArrayList al in g_List) {
...
}
}
}
This is the only 2 places the ArrayList is accessed from. The locks seem to have no effect, if i step through the code eeverything seems to work because the timing is being controlled but when i run the app normally there appears to be no data in the g_List object.
checksal
February 25th, 2005, 05:14 PM
I can think of two scenarios where it may not work.
1.) If you are trying to access a lock in a different application domain, it may not work.
2.) If you you are multi-threading, you may need to review your code.
Do not trust the debugger when threading. It has a tendency to mislead.
noods
February 25th, 2005, 05:22 PM
I'm using timers in each class, which is why I don't want to add data from class a to class b while class b is iterating through the arraylist.
I don't understand what you mean by application domain??
checksal
February 25th, 2005, 05:30 PM
Application domains provide a more secure and versatile unit of processing that the common language runtime can use to provide isolation between applications. Application domains are typically created by runtime hosts, which are responsible for bootstrapping the common language runtime before an application is run.
If an assembly is used by multiple domains in a process, the assembly's code (but not its data) can be shared by all domains referencing the assembly. This reduces the amount of memory used at run time.
This section explains more about application domains http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconapplicationdomains.asp
checksal
February 25th, 2005, 05:36 PM
I had another thought. The reason why there may be no data could be because your locks are going into deadlock. the best way to check this would be to put a messagebox in one lock and see if it gets called multiple times. If it doesnt then it would prove they are in deadlock.
noods
February 25th, 2005, 05:50 PM
Well I don't have message boxes as this is a windows service, but I have logging and ... it lookes like this:
OnTimer -- Count:0
OnTimer -- Count:0
OnTimer -- Count:0
OnTimer -- Count:0
OnTimer -- Count:0
OnTimer -- Count:0
**313 lines here from the Add**
OnTimer -- Count:313
OnTimer -- Count:0
OnTimer -- Count:0
OnTimer -- Count:0
So, it doesn't look like there is deadlocking, the lock() call is supposed to lock onto the resource and use it, if it can't get access, then it waits until it becomes available, gets access and locks everyone else out.
darwen
February 25th, 2005, 05:52 PM
There is another possibility....
SyncRoot may be passing back a different object every time you call it.
Now, I'm guessing here because I've not really gone into it in any detail, but it's certainly possible.
As far as I can tell lock() takes an object reference - i.e. a handle to an object - and in underlying code terms very probably just a unique identifier i.e. a number.
It won't take a value type (i.e. int, uint, double, or struct) - it needs a class reference.
Everything is represented in memory by an address. These addresses are passed and controlled by the GC by handles. I think - and please no flames if I'm wrong - that what 'lock' does is effectively critical section on this handle.
So internally conceptually there is a table/map of critical sections to handles, and when you perform a 'lock' this just does a lookup into this table to see if a critical section exists, and then uses that to perform the underlying synchronisation.
Now, I must re-iterate that this is the way that I see 'lock' working. I have yet to be proven wrong in all my dealings with multithreading in .NET. I may be wrong about how it works internally... but conceptually it is a theory which fits the facts.
With that in mind there are two points I would like to make :
(1) SyncRoot returns a thread-safe object for you to use. You do not need to lock it because it is already thread safe. However it is only thread safe on operations on the object which created it, and cannot be used to extert critical sections on your own code.
(2) 'lock' is used on objects as a convenience. You can use lock on any reference object (excluding value types such as integer, double, structs etc) whatsoever - but normally you use them on the object which the critical section will affect the most.
Example :
class CMyClass
{
private int m_nValue1 = 0;
private ArrayList m_aList = new ArrayList();
public int Value
{
get
{
lock (this)
{
return m_nValue1;
}
}
set
{
lock (this)
{
m_nValue1 = value;
}
}
}
public void AddValue(object obj)
{
lock (this)
{
m_aList.Add(obj);
}
}
public object GetValue(int nIndex)
{
lock (this)
{
return m_aList[nIndex];
}
}
}
This is an example of locking a class. If a thread is attempting to set/get the integer value, no other thread will be allowed to add/get values from the array list. And vice-versa.
This is fine if there is a relationship between the value and the array list.
But if there is no relationship between the value and the array list then this is inefficient because it will lead to unnecessary bottlenecks in the code.
In this case, locking could be based on the individual variables in question i.e.
class CMyClass
{
private int m_nValue1 = 0;
private ArrayList m_aList = new ArrayList();
public int Value
{
get
{
// can't lock a value type so for convenience let's lock the object
lock (this)
{
return m_nValue1;
}
}
set
{
lock (this)
{
m_nValue1 = value;
}
}
}
public void AddValue(object obj)
{
// locking the list doesn't lock the class, just the object defined
// to the lock statement
lock (m_aList)
{
m_aList.Add(obj);
}
}
public object GetValue(int nIndex)
{
lock (m_aList)
{
return m_aList[nIndex];
}
}
}
See the difference ?
This has always worked for me, although as I've said without further extensive investigation I can't be certain.
However it should help you understand how lock() works.
Darwen.
noods
February 25th, 2005, 06:03 PM
MSDN states that for an ArrayList object, you should use the SyncRoot property...
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemcollectionsarraylistclasssyncroottopic.asp
Now, looking at the logging results it looks to me like the timing is working OK. The problem now is that there is no data in the ArrayList, the count says 313 but when i go through each of them, there is no data. However, if I step through the code, there is data. Stepping through gives data, running without stepping through gives no data.
darwen
February 25th, 2005, 06:09 PM
I have written many instances of ArrayList using multithreading and I just lock the object.
i.e.
public void AddItem(object obj)
{
lock (m_aList)
{
m_aList.Add(obj);
}
}
public object GetItem(int nIndex)
{
lock (m_aList)
{
return m_aList[nIndex];
}
}
public void RemoveItem(object obj)
{
lock (m_aList)
{
m_aList.Remove(obj);
}
}
Using this I've never had a problem. Try it - and see if it works.
Darwen.
darwen
February 25th, 2005, 06:14 PM
Also see this :
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/csref/html/vclrfLockStatement.asp
Darwen.
noods
February 25th, 2005, 06:15 PM
I've tried locking on "this", the actual ArrayList, and the ArrayList.SyncRoot property.
I don't know what else to do....
basically I have an ArrayList of ArrayList's ... I add them fine, but when I go iterate through them, each ArrayList in the main collection has lost its data...
noods
February 25th, 2005, 06:16 PM
thx for all your help guys, hopefully i can get this working soon ... it's driving me nuts
darwen
February 25th, 2005, 06:25 PM
You're hitting one of the problems with multithreading. It's so really complicated.
The first question I have to ask you is : do you need your application to be multithreaded ?
Then the second question is : are you sure ?
Then the third question is : are you really sure ?
Then the fourth question is : are you really, really, really, really sure ?
Multithreading adds an order of magnitude to the complexity of your application.
It will fall over for no reason, and this will not be repeatable.
Lists (like yours) will have values you are not expecting because you haven't considered a case where one thread can reach its conclusion before another.
Multithreaded programming is not easy. You can't make any assumptions about one thread finishing before another.
If you start 10 threads at the same time - firstly you won't be starting them at the same time and secondly they will not finish in order.
I would suggest that instead of putting breakpoints in the code use System.Diagnostics.Debug.WriteLine to output exactly what is happening in your code.
Unfortunately, like Quantum Theory, the act of measuring what is happening will change the behaviour of your application.
I would bet that you're thinking about threading in a linear manner - i.e. I start threads 1,2,3 and they always execute as 1,2,3 and will always do all functions in that order - they won't.
It certainly sounds like your problem isn't concurrent access to the array list - it's more to do with the operation of the threads.
And with this, I can help you no further.
Darwen.
checksal
February 25th, 2005, 11:47 PM
I agree with Darwen. 'Data' in itself is lost when the thread dies. You may also try by using a static arraylist and see what happens.
noods
February 26th, 2005, 01:44 PM
I believe everything is working OK, as the logs from my program indicate that. The objects are never destroyed so I don't see how the data can be getting destroyed. And if it does get destroyed, then why doesn't it get destroyed when i step through the debugger.
I can't use a static ArrayList as I will have multiple objects and they can't share the ArrayList because of performance reasons
darwen
February 28th, 2005, 08:49 AM
There's two things which could be happening here :
(1) The GC isn't collecting your array list/thread when in debug mode. Have you got a 'watch' on this object in the watch window. It's possible that this is keeping the object 'alive'.
(2) Synchronisation problems with the thread which you're putting your breakpoint on. Because your suspending one of the threads when you debug it's possible that this is breaking the concurrent condition causing the problem.
If you can, post all your code for us to look at. We're just clutching at straws here without more information.
Darwen.
codeguru.com
Copyright Internet.com Inc., All Rights Reserved.