I have many threads which share access to the same pool of objects, and some threads could remove objects, so I use std::vector<std::shared_ptr<T>> to hold pointers to the objects so that objects are released when no longer needed by any threads.
I keep the size of the pool constant (the maximum amount of objects is known at start) and just zero the pointers to "removed" objects.
With the deprecation of std::atomic_...<std::shared_ptr>, I have to use class template specialization std::atomic<std::shared_ptr>, which gets me back to std::atomic which doesn't have a copy constructor and therefore can't be stored in std::vector.
So, the only way is to use the following container to deal with this:
struct T {};
std::vector<std::unique_ptr<std::atomic<std::shared_ptr<T>>>> pool;
I understand that "We can solve any problem by introducing an extra level of indirection", but this seems to be "way too much"; this is not only extra pointers and memory allocations, this makes the code hard to write and read with so many indirections.
Is there a recommended way to store a pool of objects with C++ in a multi-threaded environment?
Or, should I get back to one of the simplest solutions like storing reference counters (or states like "released", in my case) in my objects (or in a parallel array) together with:
- Storing plain objects in the array, which is really not good to combine with polymorphic class hierarchies, produces hard-to manage code, and gives hard times to use lock-free approaches.
- Store plain pointers.
Am I missing some new approach for this, which comes together with moving to std::atomic<std::shared_ptr> to have this in a straight-forward way, reusing existing stuff from C++ and std?
Of course, I can still use std::shared_ptr<T> with mutexes and so on, but I want to have a solid solution with the smallest number of used components, and approach closer to a lock-free architecture (I know that std::atomic is not lock-free) as possible.
std::atomic<std::shared_ptr<T>>can be used in a vector, it's just that you can never cop/move/resize the vector larger. If you have a fixed size, then you can create the vector with all the elements populated and then you can modify each element as you want.std::vector<std::atomic<std::shared_ptr<T>>> pool(n);should work without errors. We still need a minimal reproducible example that demonstrates errors.unique_ptr:std::make_unique<std::atomic<…>[]>(number_of_entries)simply callsnew atomic<…>[number_of_entries], which just needs a valid default-constructor. Alternatively,std::dequealso works if you needresizebut no insert in the middle, justpush_back,push_front, etc.