What is the difference between SyncRoot and Synchronized method in Queue?

G

Guest

Hi

I often use Queue.Synchronized method to create a queue for multithread writing. I also know I could use SyncRoot and lock to write Queue. Could anyone here please explain to me the pros and cons between those two approaches
Thanks a lo

Chris
 
B

Bruno Jouhier [MVP]

The Synchronized wrapper gives you the *illusion* that your code is thread
safe. As soon as you start writing code like:

for (int i = 0; i < collection.Count; i++)
DoSomething(collection)

you take chances because another thread may modify the collection (delete an
element for example) between the time you evaluate the Count property and
the time you call the indexer (so you may get an ArrayOutOfBoundException).

With SyncRoot, you use a different pattern, you synchronize in the "caller"
rather than in the "callee". This is more work because you have to worry
about synchronization in all the places where you use the collection. But on
the other hand, this forces you into putting more thought into your
synchronization strategy, and if you do it right, you will write correct
code.

For example, if you don't use the Synchronized wrapper, you have to write
the for loop above as:

lock (collection.SyncRoot)
{
for (int i = 0; i < collection.Count; i++)
DoSomething(collection)
}

This loop is safe (of course, I assume that you also have locks around every
piece of code that accesses the collection).

The SyncRoot strategy is usually more efficient too, because you don't
synchronize every call to the collection, you synchronize around blocks that
may perform several calls (the original Java collection API -- JDK 1.1 --
was terrible here because the collections were always completely
synchronized, even when you used them in single-thread scenarios).

Of course, your (high level) APIs should not expose the collections. The
collections should always be private stuff that your class uses to do its
own bookkeeping. If your APIs expose unsynchronized collections, you force
every piece of code that uses your class to do its own synchronization, and
you are in for big trouble (perf problems, race condition problems,
deadlocks).

So, my recommendation it to use SyncRoot and "external" locking rather than
the Synchronized wrapper, and to encapsulate the locking logic in classes
that don't expose their internal collections directly.

Bruno.


chrisben said:
Hi,

I often use Queue.Synchronized method to create a queue for multithread
writing. I also know I could use SyncRoot and lock to write Queue. Could
anyone here please explain to me the pros and cons between those two
approaches?
 
W

William Stacey [MVP]

I think I do get the answers I was looking for. In fact, I have thought
even SyncRoot is not thread safe for interator.

It's safe as long as all threads are locking on SyncRoot (which is really
pointing to "this" of the collection instance). So if your iterating, you
don't want another thread to be removing items at the same time - and they
can't if the other thread syncs on same lock.

The sync wrapper, truely is just a simple wrapper that itself syncs on the
SyncRoot of the Queue instance. Something very close to this:

private class SynchronizedQueue
{
private Queue q;
private object syncRoot;

internal SynchronizedQueue(Queue q)
{
this.q = q;
this.syncRoot = q.SyncRoot;
}

public override object Dequeue()
{
lock(syncRoot)
{
obj = q.Dequeue();
return obj;
}
}
}

Your effectively doing this when locking on the SyncRoot before your Gets
and Puts. Your locking the same var twice when using both SyncRoot and the
wrapper. If you can gaurentee yourself all methods will lock on SyncRoot,
then the wrapper is not needed and pretty much redundant and a bit slower
because of the twice-locked deal. hth
Cheers!
 
B

Bruno Jouhier [MVP]

Thanks.

Just one additional note:

The Synchronized wrapper may be interesting with very simple collections
like queues and stacks. If all you do with the collection is a set of
Enqueue/Dequeue or Push/Pop that only need individual synchronization, the
wrapper will be more practical, but a soon as you start doing more complex
things with collections (like iterating on their contents), you should use
SyncRoot synchronization.

Bruno.

chrisben said:
Thanks Bruno,

I think I do get the answers I was looking for. In fact, I have thought
even SyncRoot is not thread safe for interator.
 

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