ArrayList.Synchronized: what's the point?

J

Jeff Stewart

According to the documentation for ArrayList.Synchronized:

"To guarantee the thread safety of the ArrayList, all operations must be
done through this wrapper.

Enumerating through a collection is intrinsically not a thread-safe
procedure. Even when a collection is synchronized, other threads could still
modify the collection, which causes the enumerator to throw an exception. To
guarantee thread safety during enumeration, you can either lock the
collection during the entire enumeration or catch the exceptions resulting
from changes made by other threads."

To me, this reads, "To guarantee thread safety, do this, but remember that
thread safety isn't guaranteed by doing this." ??? Why would you ever use
..Synchronized rather than SyncLocking an ArrayList's SyncRoot?
 
J

Jay B. Harlow [MVP - Outlook]

Jeff,
Why would you ever use .Synchronized rather than SyncLocking an
ArrayList's SyncRoot?

Synchronized allows you to do:

Dim list As ArrayList = ArrayList.Synchronized(New ArrayList)

Which means that each method called on list will be implicitly thread safe.
Rather then explicitly wrapping each ArrayList method call with a SyncLock.
In other words your code will be smaller.

For Each involves multiple method calls into the ArrayList, making it not
thread-safe. You need to SyncLock the ArrayList before you start the For
Each, then release the lock when you are done with the For Each.

Hope this helps
Jay
 
J

Jeff Stewart

I have a class that contains an ArrayList member, and a class property,
SnapShot, that returns a Clone() of that ArrayList. Does it make more sense
to return a Synchronized Clone of the ArrayList? (In this design, it is a
foregone conclusion that the ArrayList member itself will be frequently
modified, so returning a reference to it would be bad.)
 
J

Jay B. Harlow [MVP - Outlook]

Jeff,
If my class was returning an ArrayList & the class itself had nothing more
to do with it, I would not return a Synchronized Clone, instead I would
allow the object calling the method decide if it needs a Synchronized
ArrayList or not.

Having said that if the object that owns the first ArrayList can be accessed
by multiple threads, then I would ensure that the call to Clone itself is
thread safe.

Hope this helps
Jay
 
J

Jeff Stewart

Is that any more difficult than wrapping the Clone() code in a synclock
block to prevent access to the ArrayList being cloned?
 
J

Jay B. Harlow [MVP - Outlook]

Jeff,
I'm not sure what you are asking.

Wrapping the Clone code in a Synclock is one way to make it thread safe!

If you have a Synchronized ArrayList, I would expect its Clone method to
already be thread safe.

Other methods include using other synchronization object available in the
System.Threading namespace.

Hope this helps
Jay
 
J

Jeff Stewart

You said, "...then I would ensure that the call to Clone itself is thread
safe." That's what I was referring to.
 
J

Jeff Stewart

I tinkered with ArrayList.Synchronized a bit. I tried the following code:

Module Module1
Dim arr As ArrayList = New ArrayList
Dim syncarr As ArrayList = ArrayList.Synchronized(arr)

Sub threadRunner()
syncarr.Add(42)

SyncLock arr.SyncRoot ' is necessary!
arr.Add(42)
Trace.WriteLine("Added a value in spite of synclock!")
End SyncLock
End Sub

Sub Main()
SyncLock arr.SyncRoot
Dim newThread As Threading.Thread = New
Threading.Thread(AddressOf threadRunner)
newThread.Start()
syncarr.Add(74)
Threading.Thread.CurrentThread.Sleep(System.Threading.Timeout.Infinite)
End SyncLock
End Sub

End Module

threadRunner's syncarr.Add(42) blocks forever. I'm guessing this is because
I've locked arr.SyncRoot in the main thread. So there -is- some kind of
relationship/communication between a Synchronized wrapper and its target's
SyncRoot. Is this correct?
 
J

Jay B. Harlow [MVP - Outlook]

Jeff,
Dim arr As ArrayList = New ArrayList
Dim syncarr As ArrayList = ArrayList.Synchronized(arr)
Please read the remarks of ArrayList.Synchronized:

"To guarantee the thread safety of the ArrayList, all operations
must be done through this wrapper."

To ensure the above statement was true, I would only have a single ArrayList
variable in the module, this ArrayList variable would be the ArrayList
returned from ArrayList.Synchronized, otherwise it would be way to easy to
cause problems that are extremely hard to track down...

Something like:
Dim syncarr As ArrayList = ArrayList.Synchronized(New ArrayList)

Notice the contained arraylist is not available, so there is no chance of
by-passing any synchronization logic...

Then each individual method to the ArrayList will by Synchronized. Remember
that For Each makes multiple calls into the ArrayList so its not
synchronized, using SyncLock syncarr.SyncRoot around the For Each should
make it thread safe, however you may be locking out the other threads for
too long a time. I would consider using a ReaderWriterLock depending on what
the real access to the ArrayList was...
So there -is- some kind of relationship/communication between a
Synchronized wrapper and its target's SyncRoot. Is this correct?

Reading the help for ArrayList.SyncLock that appears to be true. I normally
don't use ArrayList.Synchronized & ArrayList.SyncRoot, instead I provide my
own locking in the class that encapsulates the ArrayList.

Hope this helps
Jay
 
L

Landley

Are you saying that "For index as Int32=0 to myArrayListUpperbound" is
thread safe (or safer) than a For Each?

Landers
 
J

Jay B. Harlow [MVP - Outlook]

Landley,
No!

Although I did not explicitly mention a regular For statement, I consider
For & For Each along with Do & While to all be thread unsafe, as you are
calling into the collection multiple times.

Hope this helps
Jay
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top