关于shared_ptr参考计数块

时间:2018-11-26 03:45:34

标签: c++ multithreading c++11 shared-ptr lock-free

我对std::shared_ptr控制块有2个问题:

(1)关于大小: 如何以编程方式找到std::shared_ptr的控制块的确切大小?

(2)关于逻辑: 此外,boost::shared_ptr提到它们相对于控制块的更改是完全无锁的。(从Boost版本1.33.0开始,shared_ptr在最常见的情况下使用无锁实现平台。)我不认为std::shared_ptr遵循相同的原则-是否计划在将来的任何C ++版本中使用?这是否还意味着对于多线程情况,boost::shared_ptr是一个更好的主意?

3 个答案:

答案 0 :(得分:5)

控制块未暴露。在实现中,我已经读到,连续存储删除程序(和/或在共享共享的情况下,对象本身)的大小是动态的。

通常,它至少包含3个指针大小的字段-弱,强计数和删除程序调用程序。

至少有一个实施依赖于RTTI;其他人没有。

关于计数的操作在我阅读的实现中使用原子操作;请注意,C ++不需要原子操作都必须是无锁的(我相信没有指针大小的无锁操作的平台可以是兼容的C ++平台)。

它们的状态彼此一致,并且彼此一致,但是没有尝试使它们与对象状态一致。这就是为什么在某些平台上使用原始共享ptr作为写pImpls上的副本可能容易出错的原因。

答案 1 :(得分:5)

  

(1)关于大小:如何以编程方式找到std :: shared_ptr的控制块的确切大小?

没有办法。无法直接访问。

  

(2)关于逻辑:另外,boost :: shared_ptr提到它们对于控制块中的更改是完全无锁的。(从Boost版本1.33.0开始,shared_ptr在大多数情况下使用无锁实现。通用平台。)我不认为std :: shared_ptr遵循相同的原则-是否计划在将来的C ++版本中使用?这不是也意味着boost :: shared_ptr对于多线程情况是更好的主意吗?

绝对不是。无锁实现并不总是比使用锁的实现更好。充其量,最多不会使实现变得更糟,但不可能使实现变得更好。

请考虑两个同样胜任的程序员,每个程序员都尽最大努力实现shared_ptr。一个人必须产生一个无锁的实现。对方完全可以自由运用自己的最佳判断。在所有其他条件相同的情况下,必须产生无锁实现的方法根本不可能产生更好的实现。充其量,无锁的实现是最好的,它们都会产生一个。更糟糕的是,在此平台上,无锁实现具有巨大的缺点,并且一个实现者必须使用一个。好吧。

答案 2 :(得分:1)

(1) 当然最好检查实现,但是您仍然可以从程序中进行一些检查。

控制块是动态分配的,因此要确定其大小,您可以使新运算符重载。

然后,您还可以检查一下std :: make_shared是否为您提供了一些控制块大小的优化。 在适当的实现中,我希望它将进行两个分配(objectA和控制块):

std::shared_ptr<A> i(new A());

但是,这只会进行一次分配(然后将objectA初始化为new):

auto a = std::make_shared<A>();

考虑以下示例:

#include <iostream>
#include <memory>

void * operator new(size_t size) 
{ 
    std::cout << "Requested allocation: " << size << std::endl; 
    void * p = malloc(size); 
    return p; 
} 

class A {};

class B
{
    int a[8];
};

int main()
{
  std::cout << "Sizeof int: " << sizeof(int) << ", A(empty): " << sizeof(A) << ", B(8 ints): " << sizeof(B) << std::endl;
  {
      std::cout << "Just new:" << std::endl;
      std::cout << "- int:" << std::endl;
      std::shared_ptr<int> i(new int());
      std::cout << "- A(empty):" << std::endl;
      std::shared_ptr<A> a(new A());
      std::cout << "- B(8 ints):" << std::endl;
      std::shared_ptr<B> b(new B());
  }
  {
      std::cout << "Make shared:" << std::endl;
      std::cout << "- int:" << std::endl;
      auto i = std::make_shared<int>();
      std::cout << "- A(empty):" << std::endl;
      auto a = std::make_shared<A>();
      std::cout << "- B(8 ints):" << std::endl;
      auto b = std::make_shared<B>();
  }
}

我收到的输出(当然,它是硬件架构和编译器特定的):

Sizeof int: 4, A(empty): 1, B(8 ints): 32
Just new:
- int:
Requested allocation: 4
Requested allocation: 24

int的第一个分配-4个字节,下一个分配给控制块-24个字节。

- A(empty):
Requested allocation: 1
Requested allocation: 24
- B(8 ints):
Requested allocation: 32
Requested allocation: 24

看起来控制块是(最有可能的)24个字节。

这就是为什么要使用make_shared:

Make shared:
- int:
Requested allocation: 24

只有一个分配,即int +控制块= 24字节,比以前少。

- A(empty):
Requested allocation: 24
- B(8 ints):
Requested allocation: 48

这里可以期望56(32 + 24),但看起来实现已优化。如果使用make_shared-控制块中不需要指向实际对象的指针,并且其大小仅为16个字节。

检查控制块大小的其他可能性是:

std::cout<< sizeof(std::enable_shared_from_this<int>);

就我而言:

16

所以我要说的是,控制块的大小为16-24字节,具体取决于创建方式。