Working with a queue in .NET is an excellent experience and provides a great way to decouple execution paths. Once you start working with in memory queues your just a hope, skip, and a jump away from using "infrastructure" queues such as Service Bus, RabbitMQ, or even MSMQ on Windows Desktop.
While it is quite possible to work with the standard .NET Queue<T> object if you find that you are in need of using a lock to ensure consistent results with putting things in the queue (i.e. Enqueue) and taking things off (i.e Dequeue or Peek) or even just to obtain a consistent reliable count, well then you should probably take a look at ConcurrentQueue<T>.
There is a whole section of the .NET Framework with deals with concurrency concerns by creating specific objects for Dictionaries, Stacks, Queues and more that are considered thread safe.
When I started working with ConcurrentQueues I found it helpful to put my own wrapper around the concurrent queue object and so I've demonstrated that in the following sample which I hope others find useful.
Link to example: https://gist.github.com/briannipper/c861e708874428d4dc6dda5817411c70
*Public Domain, Link