What does Queue.Synchronized() return

  • Thread starter Thread starter Guest
  • Start date Start date
G

Guest

I have a derived class from the Queue base class. I need it to be
thread-safe, so I am using the Synchronized method (among other things out of
scope of this issue). The code below compiles, but at runtime, the cast of
"Queue.Synchronized(new EventQueue())" to an EventQueue object is failing
stating invalid cast.

Why?

public class EventQueue : System.Collections.Queue { ... }

public class AnotherClass {
private static EventQueue q = null;

public void AnotherMethod() {
q = (EventQueue)Queue.Synchronized(new EventQueue());
}
}
 
rmunson8 said:
I have a derived class from the Queue base class. I need it to be
thread-safe, so I am using the Synchronized method (among other things out of
scope of this issue). The code below compiles, but at runtime, the cast of
"Queue.Synchronized(new EventQueue())" to an EventQueue object is failing
stating invalid cast.

Why?

Because although it's returning an instance of Queue, it's not
returning an instance of EventQueue. The returned reference will access
the original EventQueue, but isn't an EventQueue in itself.

Personally I don't like the Synchronized methods anyway, as it
encourages people to reckon they don't have to think about
multi-threading any more (when you still need to place a lock round any
groups of actions which need to happen without interruption).
 
rmunson8 said:
I have a derived class from the Queue base class. I need it to be
thread-safe, so I am using the Synchronized method (among other things out
of
scope of this issue). The code below compiles, but at runtime, the cast
of
"Queue.Synchronized(new EventQueue())" to an EventQueue object is failing
stating invalid cast.

Why?

public class EventQueue : System.Collections.Queue { ... }

public class AnotherClass {
private static EventQueue q = null;

public void AnotherMethod() {
q = (EventQueue)Queue.Synchronized(new EventQueue());
}
}

It simply returns a synchronized class derived from Queue that wraps the
queue you pass. It does not nessecerily return the same type of queue as you
pass. You will have to write your own implementaion of Synchronized to do
so.
 
rmunson8 said:
I have a derived class from the Queue base class. I need it to be
thread-safe, so I am using the Synchronized method (among other things out of
scope of this issue). The code below compiles, but at runtime, the cast of
"Queue.Synchronized(new EventQueue())" to an EventQueue object is failing
stating invalid cast.

Why?

public class EventQueue : System.Collections.Queue { ... }

public class AnotherClass {
private static EventQueue q = null;

public void AnotherMethod() {
q = (EventQueue)Queue.Synchronized(new EventQueue());
}
}

From what I gather, Queue.Synchronized() returns a
Queue+SynchronizedQueue object wrapper around the Queue passed as the
method parameter. It does not return the same type as the Queue that you
are wrapping.

Either skip the type-casting at the call to Synchronized if you can get
away with using Queue-methods only, or use lock(myQueue.SyncRoot) {} if
you need access to EventQueue specific members.

/Joakim
 
Thanks. That makes sense. If I take that approach, obviously it is the
responsibility of the user of the object to perform the lock(), however, if I
have overridden several Queue methods in my derived class, would it be a
better design if I locked within those methods or leave this to the user of
the object?

public class EventQueue : System.Collections.Queue
{
public override void Enqueue(object obj)
{
lock (this.SyncRoot)
{
base.Enqueue(obj);
if (this.EventAdded != null)
{
this.EventAdded(this, new EventArgs());
}
}
}
 
rmunson8 said:
Thanks. That makes sense. If I take that approach, obviously it is the
responsibility of the user of the object to perform the lock(), however, if I
have overridden several Queue methods in my derived class, would it be a
better design if I locked within those methods or leave this to the user of
the object?

public class EventQueue : System.Collections.Queue
{
public override void Enqueue(object obj)
{
lock (this.SyncRoot)
{
base.Enqueue(obj);
if (this.EventAdded != null)
{
this.EventAdded(this, new EventArgs());
}
}
}

For a start, don't execute an event within a lock :)

See http://www.pobox.com/~skeet/csharp/threads/lockchoice.shtml

I would suggest making it the responsibility of the user to lock
appropriately unless the class is very definitely going to be used for
multi-threading (e.g. it's a producer/consumer queue with one thread
producing items and another thread consuming them).

Letting the user do the locking means you can get best performance for
an application which doesn't use any instance in more than one thread,
while a multi-threaded application will be able to choose its own
locking strategy. Make sure you document your class's thread safety
though.
 
Here is example how syncronized queue is implemented. Basically it is a
*very simple wrapper:
internal class SyncQ : Queue
{
private Queue q;
private object syncRoot;

public SyncQ(Queue q)
{
this.q = q;
this.syncRoot = this.q.SyncRoot;
}

public override void Clear()
{
lock(this.syncRoot)
{
this.q.Clear();
}
}

public override object Dequeue()
{
object obj;
lock(syncRoot)
{
obj = this.q.Dequeue();
}
return obj;
}
//And so on...
}

Naturally, you can do this yourself if you need special function. The .Net
sync wrapper should work on any queue that derives from Queue. However, I
agree with Jon and would manually sync myself as you will probably need to
anyway at different points as your complexity grows. Say you want to check
the Count and then Dequeue only if count is > 0. You need to lock SyncRoot
anyway even if using a Sync Wrapper as you want the count test and the
Dequeue operation to be one atomic operation. So in this case, you end up
with three Monitor locks, the manual SyncRoot you do yourself, and the two
locks the SyncWrapper does (one for Count "Get" and one for the Dequeue
method). It is the same Monitor object, but a lock is taken and released
three times. There are others you will run into and others that could
result in even more locks being taken and released. This is one example of
how simple .Net can be, but yet can hurt our perf if we don't consider what
is really happening under the covers. Just some thoughts. Cheers!
 
Back
Top