System.Collections.Concurrent provides a bunch of thread-safe collections that you can use when doing asynchronous programming without worrying about bad stuff happening.
The simplest ones, ConcurrentBag<T> (which allows you to put stuff in and take things out in an unordered way) and ConcurrentDictionary<TKey, TValue> (a key/value set) are simple and easy to use, but there isn’t really a built-in way to facilitate an ordered / searchable list.
Creating one from scratch with locks or semaphores can be error-prone, especially if you’re not a concurrency expert. Luckily, we can simply leverage the existing ConcurrentDictionary<TKey, TValue> to suit our needs – simply create a new class with a private ConcurrentDictionary to do the heavy thread-safe lifting.
To make things easier, I looked at the interfaces that are implemented by a regular HashSet, and made this class also implement the ones that made sense. Then I just went through and filled out the methods using the .Keys property from the dictionary.