lock problem

  • Thread starter Thread starter Sharon
  • Start date Start date
S

Sharon

Hi all.
I have an ArrayList and
sometimes while enumerating through, i get an
exception because another thread has added to the ArrayList.
To solve this problem, i lock the enumeration,
passing the ArrayList instance to the lock.
But i still get the same exception.
Thanks for your help.
Sharon.
 
Sharon said:
The ArrayList.

Are the other threads locking the arraylist when they insert items?

Are you maintaining the lock for the duration of the enumeration?

Daviv
 
Should i try it this way:
lock(ArrayList.Synchronized(arrayListInstance))
{
enumerate...
}

And not this way:
lock(arrayListInstance)
{
enumerate...
}
 
Nop... I owe you an apology. I wrote Syncronized instead of SyncRoot :$

I meant something like this...

ArrayList al = new ArrayList();

lock(al.SyncRoot) {
foreach(object item in al) {
// Enumerate
}
}


Regards
 
I have to say that I think lock to be maybe the worst named keyword in the language. It gives the impression that it imposes some kind of invisible force field around the object in question that cannot be pierced by other code until the code block executes. This unfortunately is rubbish.

lock(obj)
{
// some thread sensitive code here
}

translates to

Monitor.Enter(obj);
try
{
// some thread sensitive code here
}
finally
{
Monitor.Exit(obj);
}

and all Monitor.Enter (simplistically) does is manipulate the SyncBlock in the objects header. Conceptually It stamps its thread id against the SyncBlock. If another thread id is already stamped there it blocks until the SyncBlock becomes "un-owned" then proceeds to stamp its thread id against the SyncBlock. Monitor.Exit clears the threads id from the syncblock.

So simply stating lock() on anything at all (even if it is the SyncRoot) will achieve nothing UNLESS all code that is accessing the rescource ALSO calls lock on the same thing. In other words

lock(obj)
{
// access some shared. thread sensitive resource
}

only guarantees thread synchronization if all code calls lock(obj), obj being the same physical object as above, before accessing the shared resource.

With collections you can create a thread safe wrapper wound the collection using the Synchronized method - but all this does is do the appropriate locking internally.

One final thing about lock. lock is a block with no timeout. If you get a thread deadlock your application is hosed. The Monitor class also has a TryEnter method which you can pass a timeout to. Ian Griffiths has written a nice wrapper for this which allows you to use stcks based release using the "using" keyword. Heres the URL

http://www.interact-sw.co.uk/iangblog/2004/03/23/locking

Regards

Richard Blewett - DevelopMentor
http://staff.develop.com/richardb/weblog

nntp://news.microsoft.com/microsoft.public.dotnet.languages.csharp/<[email protected]>

Nop... I owe you an apology. I wrote Syncronized instead of SyncRoot :$

I meant something like this...

ArrayList al = new ArrayList();

lock(al.SyncRoot) {
foreach(object item in al) {
// Enumerate
}
}


Regards



Sharon said:
Should i try it this way:
lock(ArrayList.Synchronized(arrayListInstance))
{
enumerate...
}

And not this way:
lock(arrayListInstance)
{
enumerate...
}



---
Incoming mail is certified Virus Free.
Checked by AVG anti-virus system (http://www.grisoft.com).
Version: 6.0.770 / Virus Database: 517 - Release Date: 27/09/2004



[microsoft.public.dotnet.languages.csharp]
 
I realise that my post was incomplete - I didn't address the SyncRoot issue. Take the following code:

static void Main(string[] args)
{
arr.Add(new object());
arr.Add(new object());
arr.Add(new object());
arr.Add(new object());
arr.Add(new object());
arr.Add(new object());

Thread t = new Thread(new ThreadStart(Ouch));
t.Start();

lock(arr.SyncRoot)
{
foreach(object o in arr)
{
Thread.Sleep(1000);
o.ToString();
}
}
}

static void Ouch()
{
Thread.Sleep(1000);
arr.RemoveAt(0);
}

This throws an InvalidOperationException because the collection was modified during iteration by the Ouch method running on the other thread. In other words, the SyncRoot is useless on its own in the default case. However, add the following line of code just before the creation of the Thread object (or at least before the thread is started).

arr = ArrayList.Synchronized(arr);

This creates a thread safe wrapper around the arraylist with operations locking the SyncRoot internally. Now the code will perform correctly as the foreach is synchronized with the modifications.

Regards

Richard Blewett - DevelopMentor
http://staff.develop.com/richardb/weblog

nntp://news.microsoft.com/microsoft.public.dotnet.languages.csharp/<[email protected]>

I have to say that I think lock to be maybe the worst named keyword in the language. It gives the impression that it imposes some kind of invisible force field around the object in question that cannot be pierced by other code until the code block executes. This unfortunately is rubbish.

lock(obj)
{
// some thread sensitive code here
}

translates to

Monitor.Enter(obj);
try
{
// some thread sensitive code here
}
finally
{
Monitor.Exit(obj);
}

and all Monitor.Enter (simplistically) does is manipulate the SyncBlock in the objects header. Conceptually It stamps its thread id against the SyncBlock. If another thread id is already stamped there it blocks until the SyncBlock becomes "un-owned" then proceeds to stamp its thread id against the SyncBlock. Monitor.Exit clears the threads id from the syncblock.

So simply stating lock() on anything at all (even if it is the SyncRoot) will achieve nothing UNLESS all code that is accessing the rescource ALSO calls lock on the same thing. In other words

lock(obj)
{
// access some shared. thread sensitive resource
}

only guarantees thread synchronization if all code calls lock(obj), obj being the same physical object as above, before accessing the shared resource.

With collections you can create a thread safe wrapper wound the collection using the Synchronized method - but all this does is do the appropriate locking internally.

One final thing about lock. lock is a block with no timeout. If you get a thread deadlock your application is hosed. The Monitor class also has a TryEnter method which you can pass a timeout to. Ian Griffiths has written a nice wrapper for this which allows you to use stcks based release using the "using" keyword. Heres the URL

http://www.interact-sw.co.uk/iangblog/2004/03/23/locking

Regards

Richard Blewett - DevelopMentor
http://staff.develop.com/richardb/weblog

nntp://news.microsoft.com/microsoft.public.dotnet.languages.csharp/<[email protected]>

Nop... I owe you an apology. I wrote Syncronized instead of SyncRoot :$

I meant something like this...

ArrayList al = new ArrayList();

lock(al.SyncRoot) {
foreach(object item in al) {
// Enumerate
}
}


Regards



Sharon said:
Should i try it this way:
lock(ArrayList.Synchronized(arrayListInstance))
{
enumerate...
}

And not this way:
lock(arrayListInstance)
{
enumerate...
}



---
Incoming mail is certified Virus Free.
Checked by AVG anti-virus system (http://www.grisoft.com).
Version: 6.0.770 / Virus Database: 517 - Release Date: 27/09/2004



[microsoft.public.dotnet.languages.csharp]

---
Incoming mail is certified Virus Free.
Checked by AVG anti-virus system (http://www.grisoft.com).
Version: 6.0.770 / Virus Database: 517 - Release Date: 27/09/2004



[microsoft.public.dotnet.languages.csharp]
 
Richard Blewett said:
I realise that my post was incomplete - I didn't address the SyncRoot
issue. Take the following code:

static void Main(string[] args)
{
arr.Add(new object());
arr.Add(new object());
arr.Add(new object());
arr.Add(new object());
arr.Add(new object());
arr.Add(new object());

Thread t = new Thread(new ThreadStart(Ouch));
t.Start();

lock(arr.SyncRoot)
{
foreach(object o in arr)
{
Thread.Sleep(1000);
o.ToString();
}
}
}

static void Ouch()
{
Thread.Sleep(1000);
arr.RemoveAt(0);
}

This throws an InvalidOperationException because the collection was
modified during iteration by the Ouch method running on the other
thread. In other words, the SyncRoot is useless on its own in the
default case. However, add the following line of code just before the
creation of the Thread object (or at least before the thread is
started).

arr = ArrayList.Synchronized(arr);

This creates a thread safe wrapper around the arraylist with
operations locking the SyncRoot internally. Now the code will perform
correctly as the foreach is synchronized with the modifications.

Note that you don't have to use ArrayList.Synchronized to use SyncRoot
though - if Ouch() had been written:

static void Ouch()
{
Thread.Sleep(1000);
lock (arr.SyncRoot)
{
arr.RemoveAt(0);
}
}

it would have been fine.

Personally I don't like ArrayList.Synchronized - I think it's better to
make it obvious when you're locking like this. That's just MHO though.
 
But in that case there is nothing special about the SyncRoot and everyone may as well just lock the ArrayList itself. The SyncRoot only has a unique purpose if the ArrayList (or other collection) is Synchronized.

Regards

Richard Blewett - DevelopMentor
http://staff.develop.com/richardb/weblog

nntp://news.microsoft.com/microsoft.public.dotnet.languages.csharp/<[email protected]>

Richard Blewett said:
I realise that my post was incomplete - I didn't address the SyncRoot
issue. Take the following code:

static void Main(string[] args)
{
arr.Add(new object());
arr.Add(new object());
arr.Add(new object());
arr.Add(new object());
arr.Add(new object());
arr.Add(new object());

Thread t = new Thread(new ThreadStart(Ouch));
t.Start();

lock(arr.SyncRoot)
{
foreach(object o in arr)
{
Thread.Sleep(1000);
o.ToString();
}
}
}

static void Ouch()
{
Thread.Sleep(1000);
arr.RemoveAt(0);
}

This throws an InvalidOperationException because the collection was
modified during iteration by the Ouch method running on the other
thread. In other words, the SyncRoot is useless on its own in the
default case. However, add the following line of code just before the
creation of the Thread object (or at least before the thread is
started).

arr = ArrayList.Synchronized(arr);

This creates a thread safe wrapper around the arraylist with
operations locking the SyncRoot internally. Now the code will perform
correctly as the foreach is synchronized with the modifications.

Note that you don't have to use ArrayList.Synchronized to use SyncRoot
though - if Ouch() had been written:

static void Ouch()
{
Thread.Sleep(1000);
lock (arr.SyncRoot)
{
arr.RemoveAt(0);
}
}

it would have been fine.

Personally I don't like ArrayList.Synchronized - I think it's better to
make it obvious when you're locking like this. That's just MHO though.

--
Jon Skeet - <[email protected]>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too

---
Incoming mail is certified Virus Free.
Checked by AVG anti-virus system (http://www.grisoft.com).
Version: 6.0.770 / Virus Database: 517 - Release Date: 27/09/2004



[microsoft.public.dotnet.languages.csharp]
 
Richard Blewett said:
But in that case there is nothing special about the SyncRoot and
everyone may as well just lock the ArrayList itself. The SyncRoot
only has a unique purpose if the ArrayList (or other collection) is
Synchronized.

Well, unless the collection is derived from another one - eg the
collection returned by Hashtable.Values has the same SyncRoot as the
hashtable itself.
 
Hi,

I suggest you this way:

//Get a sync copy

ArrayList arSynced = ArrayList.Synchronized( theArrayList )

It does assure sync using ArrayList.SyncRoot , now you can safety do

lock( arSynced.SyncRoot )
{
//enumerate
}

if another thread do a arSynced.Add( object ) it will work OK !!


Cheers,
 
Thanks for your help.
As i understand it, the Synchronized wrapper uses the collection SyncRoot
on every call.
And that is why, using only the wrapper will not work in this case.
Because the collection is not locked between enumerator internal operations.
Using the wrapper and locking SyncRoot during enumeration will work,
but i thinks it's better, to lock SyncRoot during enumeration,
and use SyncRoot when adding or removing.
Sharon.
 
Sharon said:
As i understand it, the Synchronized wrapper uses the collection SyncRoot
on every call.
And that is why, using only the wrapper will not work in this case.
Because the collection is not locked between enumerator internal operations.
Using the wrapper and locking SyncRoot during enumeration will work,
but i thinks it's better, to lock SyncRoot during enumeration,
and use SyncRoot when adding or removing.

Exactly.
 
Back
Top