7

I have generic Queue<T> (System.Collections.Generic) which is accessed for writing from one thread. And it must be accessed from another thread for reading.

I don't want to do any process synchronization (which includes using ConcurrentQueue<T>) for performance reasons. So I came up with idea to copy the entire queue to another queue object of same type in the reading thread. Subsequent operations in the reading thread will be done on the copy. Copying will be done with simple operator =.

Here is some pseudo-code:

//Creating main queue
Queue<MyType> queue1 = new Queue<MyType>();

Writing thread:

//Perform writing in the main queue
queue1.Enqueue(object);
...
queue1.Dequeue();

Reading thread:

//Copy main queue 
Queue<MyType> queue2 = queue1;
//perform all operations in reading thread on queue2

So is such solution thread safe?

UPD: Thank you very much, I wasn't aware that this is merely copying of the link. So Is there a way to copy entire object by value in thread-safe manner?

CodesInChaos
  • 106,488
  • 23
  • 218
  • 262
  • It is only thread safe when you don't change the queue after you published it to another thread. – Steven Feb 13 '12 at 17:39
  • Usually, a `Queue` is meant to be used in a producer/consumer scenario. I.e., one thread writes to it, and another one reads from it. It doesn't make much sense to copy it every time (you can use any collection type, and it will work the same). You should either use a single shared queue and lock it shortly while you dequeue a single item, or use a `ConcurrentQueue` like said below. – vgru Feb 13 '12 at 18:38

5 Answers5

11

Queue<T> is a reference type. So assigning queue1 to queue2 does only copy the reference, not the queue itself.

The assignment itself is atomic and thus thread-safe. Accessing queue1 in one thread, and queue2 in another is no safer that accessing queue1 from both of them. i.e. it is unsafe.

I believe ConcurrentQueue<T> uses "lockless" programming techniques (Interlocked.Exchange and friends) and is pretty fast. You should benchmark it first, before excluding it as a solution.

Copying a Queue<T> will certainly be slower than just using ConcurrentQueue<T>.


On my 2.6GHz system ConcurrentQueue<object> manages 15 million enqueue/dequeue pairs per second compared to 40 million with with Queue<object>. So Queue<object> is about three times as fast.

200 CPU cycles for an enqueue/dequeue pair is pretty cheap. If that is the bottleneck, try using more granular items in the queue.

CodesInChaos
  • 106,488
  • 23
  • 218
  • 262
2

That doesn't copy the instance of Queue. It only copies the reference itself. Copying the reference is atomic, but the new reference will still point to the same instance. Modifying the instance from multiple threads is not thread safe without synchronization.

Brian Rasmussen
  • 114,645
  • 34
  • 221
  • 317
1

Short answer -- no, it's not thread-safe.

Note that you're not copying the queue itself: you're copying a reference to a single queue. (Reference assignment is atomic so your queue2 = queue1 line isn't the problem. It's what you subsequently do with the queue that isn't thread-safe at all.)

LukeH
  • 263,068
  • 57
  • 365
  • 409
0

Copying a collection this way will only cause shallow copy of the object. This means it will only copy the reference to the same Queue. This is thread safe.

If your intention is to do a deep copy. See it this post can help you on performing a deep copy of the object. Although @CodeInChaos has a really good point. Copying the whole object this way will certainly be slower than just using ConcurentQueue<T>.

Community
  • 1
  • 1
TheBoyan
  • 6,802
  • 3
  • 45
  • 61
  • 1
    That kind of deep copy is very ugly. And it's certainly slower than just using `ConcurrentQueue`. – CodesInChaos Feb 13 '12 at 17:37
  • @CodeInChaos - I know, but I think he mentions in his post that he doesn't want to use ConcurrentQueue, that's why I didn't suggest it. – TheBoyan Feb 13 '12 at 17:38
0

Here's what MSDN has to say about thread safety:

A Queue can support multiple readers concurrently, as long as the collection is not modified. Even so, enumerating through a collection is intrinsically not a thread-safe procedure. To guarantee thread safety during enumeration, you can lock the collection during the entire enumeration. To allow the collection to be accessed by multiple threads for reading and writing, you must implement your own synchronization.

Dennis Traub
  • 50,557
  • 7
  • 93
  • 108