Why do I need to lock shared_ptr with multiple threads?

Source: Internet
Author: User

Shared_ptr's Data Structure shared_ptr is a reference counting smart pointer. Almost all implementations use a count value (count) on the heap) (in theory, there is also a method of circular linked list, but there is no instance ). Specifically, shared_ptr <Foo> contains two members: one is the pointer ptr pointing to Foo, and the other is the ref_count pointer (its type is not necessarily the original pointer, it may be the class type, but it does not affect the discussion here), pointing to the ref_count object on the stack. The ref_count object has multiple members, as shown in Data Structure 1. deleter and allocator are optional. Figure 1: shared_ptr data structure. In order to simplify and highlight the focus, only the values of use_count are shown below: the above values are shared_ptr <Foo> x (new Foo); the corresponding memory data structure. If you execute shared_ptr <Foo> y = x;, the corresponding data structure is as follows. However, y = x involves copying two members. The two copies do not occur simultaneously (atomic. Step 1: copy the ptr pointer. Step 2: copy the ref_count pointer. As a result, add 1 to the reference count: steps 1 and 2 are sequentially related to the implementation (therefore, y is not drawn in step 2. ptr point), all I have seen are first 1 and then 2. Since y = x has two steps, without mutex protection, race condition exists in multithreading. The race condition that may occur in multi-thread unprotected read/write shared_ptr is considered as a simple scenario. There are three shared_ptr <Foo> objects x, g, n: shared_ptr <Foo> g (new Foo ); // shared_ptrshared_ptr shared between threads <Foo> x; // The local variable shared_ptr of thread A <Foo> n (new Foo); // start with the local variable of thread B, security issues. Thread A executes x = g; (that is, read g). The following steps are completed: 1. Then thread B is switched. At the same time, programming B to execute g = n; (that is, write g), the two steps are completed together. Step 1: Step 2: The Foo1 object has been destroyed, and x. ptr has become an empty hanging pointer! Finally, return to thread A and complete Step 2: multi-thread read/write g without protection, resulting in "x is an empty Pointer. This is the reason why multiple threads Must lock the same shared_ptr. Of course, race condition is much more than this type. interweaving may cause other errors. Think about race condition if the operator = Implementation of shared_ptr is to copy ref_count (step 2) and then replicate ptr (step 1? Miscellaneous shared_ptr is the key of unordered_map. If you put boost: shared_ptr in unordered_set or the key used for unordered_map, be careful that the hash table degrades to the linked list until Boost 1.47.0 is released, unordered_set <std:: shared_ptr <T> although it can be compiled, its hash_value is the result of implicit conversion of shared_ptr to bool. That is to say, if the hash function is not customized, unordered _ {set/map} degrades to a linked list. Boost 1.51 adds overload in boost/functional/hash/extensions. hpp. Now, you only need to include this header file to use unordered_set safely and efficiently <std: shared_ptr>. This is also the reason why the hash_value (const boost: shared_ptr <T> & x) function needs to be defined in the muduo examples/idleconnection example (section 7.10.2, p.255 ). Because Debian 6 Squeeze and boost versions in Ubuntu 10.04 LTS all have this bug. Why does ref_count in Figure 1 also have a pointer to Foo? Shared_ptr <Foo> sp (new Foo) captures the destructor of Foo when constructing the sp. In fact, shared_ptr.ptr and ref_count.ptr can be of different types (as long as there is implicit conversion between them), which is a major function of shared_ptr. In three points: 1. Virtual destructor is not required. Assume that Bar is the base class of Foo, but Bar and Foo do not have virtual destructor. Shared_ptr <Foo> sp1 (new Foo); // The ref_count.ptr type is Foo * shared_ptr <Bar> sp2 = sp1; // It can be assigned a value and is automatically transformed up to cast) sp1.reset (); // when the reference count of the Foo object is reduced to 1, sp2 can still safely manage the lifecycle of the Foo object and safely and completely release the Foo object, because its ref_count remembers the actual type of Foo. Www.2cto.com 2. shared_ptr <void> can point to and securely manage (destructor or prevent destructor) any object; tie () of muduo: net: Channel class () the function uses this feature to prevent premature object analysis. For details, see section 7.15.3. Shared_ptr <Foo> sp1 (new Foo); // The type of ref_count.ptr is Foo * shared_ptr <void> sp2 = sp1; // values can be assigned, foo * automatically transforms to void * sp1.reset (); // when the reference count of the Foo object is reduced to 1, sp2 can still safely manage the lifecycle of the Foo object and safely and completely release Foo, the delete void * is not displayed because the delete operation is ref_count.ptr, not sp2.ptr. 3. Multi-inheritance. If Bar is one of multiple base classes of Foo, shared_ptr <Foo> sp1 (new Foo); shared_ptr <Bar> sp2 = sp1; // In this case, sp1.ptr and sp2.ptr may point to different addresses, because the offset of Bar subobject in Foo object may not be 0. Sp1.reset (); // at this time, the reference count of the Foo object is reduced to 1, but sp2 can still safely manage the lifecycle of the Foo object, and safely and completely release the Foo, because the delete is not Bar *, it is the original Foo *. In other words, sp2.ptr and ref_count.ptr may have different values (of course, their types are also different ). Why do we try to use make_shared ()? In order to save one memory allocation, the original shared_ptr <Foo> x (new Foo); each of Foo and ref_count needs to be allocated with one memory allocation. Now, if make_shared () is used, A large enough memory can be allocated at a time for the Foo and ref_count objects. The data structure is: But the Foo constructor parameter must be passed to make_shared (), and the latter must be passed to Foo: Foo (), this can be solved perfectly only through perfect forwarding in C ++ 11.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.