ساختمان داده ConcurrentQueue رو پیشنهاد می کنم. یک صف هست که چندین ترد می تونن همزمان به اون آیتم اضافه می کنن.
اضافه کردن با متد Enqueue هست و برداشتن از صف با متد TryDequeue.
تمام هماهنگی ها به طور داخلی در طراحی این کلاس در نظر گرفته شده و شما مسئول Sync ترد ها نیستید و استفاده از Monitor منتفی هست.

البته اگر اصرار بر FIFO بودن دارید باید از صف استفاده کنید چون آیتمی که آخر وارد می شود حتماً اول برداشته می شود.


ConcurrentQueue<T> handles all synchronization internally. If two threads call TryDequeue at precisely the same moment, neither operation is blocked. When a conflict is detected between two threads, one thread has to try again to retrieve the next element, and the synchronization is handled internally.
TryDequeue tries to remove an element from the queue. If the method is successful, the item is removed and the method returns true; otherwise, it returns false. That happens atomically with respect to other operations on the queue. If the queue was populated with code such as q.Enqueue("a"); q.Enqueue("b"); q.Enqueue("c"); and two threads concurrently try to dequeue an element, one thread will dequeue a and the other thread will dequeue b. Both calls to TryDequeue will return true, because they were both able to dequeue an element. If each thread goes back to dequeue an additional element, one of the threads will dequeue c and return true, whereas the other thread will find the queue empty and will return false.