问题
std::shared_ptr
needs to allocate a control block on the heap which holds the reference count. There was another approach I learnt from http://ootips.org/yonat/4dev/smart-pointers.html which keeps all the references in a doubly linked list. It doesn't need additional allocations nor a counter but the reference object itself is larger.
Is there a benchmark or any clear reason showing one implementation is better than the others?
回答1:
The standard does in theory allow a linked list to be used, but because copying a shared_ptr
must be threadsafe it would be harder to implement that with a linked list. The list would need to be protected by a mutex (or be a lockfree list, which is much harder) so that every time a shared_ptr
is copied or goes out of scope the list can be safely modified by multiple threads.
It's much simpler and in general more efficient to do reference counting using atomic types and use atomic operations for the ref count updates.
Edit: To answer the comment below, just using atomic pointers to implement the linked list wouldn't be enough. To add or remove a node from the list (which correspond to increasing and decreasing the use_count
) you would need to update two pointers atomically, to adjust the links in the nodes before and after the one being added/removed. std::atomic<T*>
allows you to update a single pointer atomically, but doesn't help if you need to update two such objects atomically. Since those two pointers live in separate nodes they're not adjacent so even a quadword CAS won't help.
Alternatives are a mutex protecting the whole list (obviously bad for contention) or a mutex per list node where you lock the mutex of each node involved in any update, which uses more memory and affects up to three nodes at a time, i.e. requires locking three mutexes. If you have use_count()
less than or equal to five then copying/destroying any one shared_ptr
contends with copying/destroying any of the other instances that shares ownership of the same pointer. You might get less contention with high use counts where most updates are to non-neighbour nodes distant from each other, but probably not in the general case. Plenty of programs use shared_ptr
with use counts in single digits. Even when use counts are high and there's no contention on any nodes, you still have to lock three mutexes and create/destroy a list node (possibly requiring a heap allocation) and update the pointers in its neighbouring nodes, so an atomic increment/decrement is much simpler and could still be faster despite the contention on the atomic integers.
Last time I mentioned on the committee reflector that shared_ptr
isn't required to use ref counts and could use a list I got the replies:
Does anyone actually do that, given that the Standard recognizes multithreading now?
and (in reference to the thread-safety requirements):
It's much harder to make that work (efficiently) for a reference-linked implementation. I'm not even sure that it's possible, though it may be.
来源:https://stackoverflow.com/questions/14291744/why-doesnt-stdshared-ptr-use-reference-linking