Great editing
ConcurrentQueue is only safe for Enqueue (T) and T Dequeue () operations. You do foreach and it does not sync at the required level. The biggest problem in your particular case is the fact that listing the queue (which is the property of the Property) may result in the well-known exception "Collection has been changed." Why is this the biggest problem? Since you add things to the queue after , you added the corresponding objects to the list (the synchronization of the list is also very necessary there, but this + the biggest problem will be solved with only one "bullet"). When enumerating a collection, it is not easy to learn the fact that another thread modifies it (even if at the microscopic level the modification is safe - ConcurrentQueue does just that).
Therefore, you absolutely need to synchronize access to the queues (and the central list when you are on it) using another synchronization tool (and I mean that you can also forget about ConcurrentQueue and use a simple queue or even a List as long as you never share things).
So just do something like:
public void Writer(object toWrite) { this.rwLock.EnterWriteLock(); try { int tailIndex = this.list.Count; this.list.Add(toWrite); if (..condition1..) this.queue1.Enqueue(tailIndex); if (..condition2..) this.queue2.Enqueue(tailIndex); if (..condition3..) this.queue3.Enqueue(tailIndex); ..etc.. } finally { this.rwLock.ExitWriteLock(); } }
and in AccessItems:
public IEnumerable<object> AccessItems(int queueIndex) { Queue<object> whichQueue = null; switch (queueIndex) { case 1: whichQueue = this.queue1; break; case 2: whichQueue = this.queue2; break; case 3: whichQueue = this.queue3; break; ..etc.. default: throw new NotSupportedException("Invalid queue disambiguating params"); } List<object> results = new List<object>(); this.rwLock.EnterReadLock(); try { foreach (var index in whichQueue) results.Add(this.list[index]); } finally { this.rwLock.ExitReadLock(); } return results; }
And, based on my understanding of when your application is accessing a list and various queues, it should be 100% safe.
End of big editing
First of all: What do you call Thread-Safe? Eric Lippert
In your particular case, I think the answer is no .
In the global context (actual list), inconsistencies may occur.
Instead, it is possible that actual readers (who can very โencounterโ a unique writer) turn out to be inconsistent on their own (their own stacks mean: local variables of all methods, parameters, as well as their logically isolated part of the heap)).
The possibility of such "inconsistencies" for the stream (the N-th stream wants to know the number of elements in the List and finds that this value is 39404999, although in fact you added only 3 values) is enough to declare that, as a rule, saying that the architecture It is not thread safe (although you are not actually modifying the globally accessible List by simply reading it in error).
I suggest you use the ReaderWriterLockSlim class. I think you will find it according to your needs:
private ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); private List<Object> items; private ConcurrentQueue<int> queue; private Timer timer; private void callback(object state) { int index = items.Count; this.rwLock.EnterWriteLock(); try { // in this place, right here, there can be only ONE writer // and while the writer is between EnterWriteLock and ExitWriteLock // there can exist no readers in the following method (between EnterReadLock // and ExitReadLock) // we add the item to the List // AND do the enqueue "atomically" (as loose a term as thread-safe) items.Add(new object()); if (true)//some condition here queue.Enqueue(index); } finally { this.rwLock.ExitWriteLock(); } timer.Change(TimeSpan.FromMilliseconds(500), TimeSpan.FromMilliseconds(-1)); } //This can be called from any thread public IEnumerable<object> AccessItems() { List<object> results = new List<object>(); this.rwLock.EnterReadLock(); try { // in this place there can exist a thousand readers // (doing these actions right here, between EnterReadLock and ExitReadLock) // all at the same time, but NO writers foreach (var index in queue) { this.results.Add ( this.items[index] ); } } finally { this.rwLock.ExitReadLock(); } return results; // or foreach yield return you like that more :) }