Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/147.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ C+中具有定义良好的分配器的危险指针+;_C++_Multithreading_Concurrency - Fatal编程技术网

C++ C+中具有定义良好的分配器的危险指针+;

C++ C+中具有定义良好的分配器的危险指针+;,c++,multithreading,concurrency,C++,Multithreading,Concurrency,我在书的第7章(第194页)中读到了以下注释。我的问题是,在标准库中是否有任何分配器,我们在中所做的工作都有很好的定义 使用这样的危险指针依赖于这样一个事实,即在指针引用的对象被删除后使用指针的值是安全的。如果您使用的是new和delete的默认实现,那么这在技术上是未定义的行为,因此您需要确保实现允许它,或者需要使用允许这种使用的自定义分配器 是我在这里谈论的未定义/未指定行为的相关帖子。上述注释所指的代码如下: std::shared_ptr<T> pop() { std::

我在书的第7章(第194页)中读到了以下注释。我的问题是,在标准库中是否有任何分配器,我们在中所做的工作都有很好的定义

使用这样的危险指针依赖于这样一个事实,即在指针引用的对象被删除后使用指针的值是安全的。如果您使用的是new和delete的默认实现,那么这在技术上是未定义的行为,因此您需要确保实现允许它,或者需要使用允许这种使用的自定义分配器

是我在这里谈论的未定义/未指定行为的相关帖子。上述注释所指的代码如下:

std::shared_ptr<T> pop() {
  std::atomic<void*>& hp=get_hazard_pointer_for_current_thread();
  node* old_head=head.load();
  node* temp;
  do {
    temp=old_head;         // Here we are reading (not dereferencing) a pointer that might have been deleted already
    hp.store(old_head);    // And here
    old_head=head.load();
  } while(old_head!=temp); // And here
  // ...
}
这就是所谓的“内存回收问题”——只要某个线程仍然持有对某个对象的引用,因此可能仍然访问该对象,就不能删除该对象。如果一个线程想要“释放”某个对象,它通常不会直接调用
delete
,而是调用某个
retire
函数,该函数存储指针并将回收延迟到以后安全时(即,在危险指针的情况下,直到没有线程持有该对象的HP为止)。由于扫描所有线程的危险指针相当昂贵,因此只有在失效对象的数量达到某个阈值时才进行扫描。通过这种方式,完全可以使用定义良好的行为实现危险指针

pop操作的代码缺少一些重要的细节-您将要替换head,但如何处理旧值?你如何回收它?你只需要调用delete吗

AR.D.RoBixon提出了C++标准的内存回收方案的通用接口。一般的想法是,您有一个

guard\u ptr
的概念,它保存对对象的安全引用(类似于
share\u ptr
)。只要任何线程持有一个对象的此类保护,该对象就不能被删除,并且您只能在获取此类
guard\u ptr
后才能访问该对象。如果要回收对象,只需调用
guard\u ptr::retire
。然后,一旦对象安全,它最终将被底层实现删除

引用计数是一个完全不同的故事,因为不可能在单个原子操作中加载指针并增加ref计数器。这就是为什么使用无锁引用计数永远不可能将失效对象返回内存管理器的原因,因为ref计数器必须无限期地可用。相反,节点通常保存在一个空闲列表中,并被重用

关于使用引用计数的推送操作,这是完全安全的。while循环不断尝试使用
new\u node
更新
head
,其中
new\u node.ptr->next
是预期值。如果CAS失败,当前值将存储在
new\u node.ptr->next
中,但这没有问题,因为CAS失败了
new\u node
不可见,因此其他线程无法访问它。您认为我们正在哪里读取可能已被删除的指针

void push(T const& data) {
  counted_node_ptr new_node;
  new_node.ptr=new node(data);
  new_node.external_count=1;
  new_node.ptr->next=head.load(std::memory_order_relaxed)
  while(!head.compare_exchange_weak(
                new_node.ptr->next,
                new_node,
                std::memory_order_release,
                std::memory_order_relaxed)); }
如果你对这个话题感兴趣,我可以让你参考我的论文。它不仅对大量填海方案进行了一般性讨论,还详细介绍了我自己根据Arch D.Robison提出的上述接口的改编版本实施的各种填海方案

基于我在本论文中的工作,我创建了一个开源库,提供各种回收方案(包括危险指针和无锁引用计数)和并发数据结构的实现:

更新
关于你所说的“读取(而不是取消引用)已释放指针的值通常是未定义的”这句话,我没有这本书,所以我不确定作者指的是什么。我不知道为什么引用的SO答案中的authro声明“使用”包括“复制”的值。C++17标准对无效指针值作了如下说明:

通过 无效指针值和将无效指针值传递给解除分配函数具有未定义的行为。对无效指针值的任何其他使用都有实现定义的行为

作为脚注:

一些实现可能会确定复制无效指针值会导致系统生成的运行时故障

可能出现运行时故障的原因是,过去的一些体系结构使用专用地址寄存器加载和存储指针,例如,如果指针中的段号当前未映射,它们可能会出现故障。但是,我不知道任何当前的体系结构或编译器在读取无效指针值时会导致这样的错误

pointer\u safety
概念是在C++11中添加的,作为垃圾收集器接口的一部分(请参见提案)。Bjarne Stroustrup的《C++程序设计语言》(第四版)讲述了以下关于<代码> PoTeNeToalEng/<代码>值:

  • 放松:安全派生和非安全派生指针被视为等价的。收集每个没有安全派生或可跟踪指针的对象
  • 首选:与relaxed类似,但垃圾收集器可能作为泄漏检测器和/或“坏指针”解引用检测器运行
  • 严格:安全派生和非安全派生的指针可能会被区别对待;也就是说,垃圾收集器可能正在运行,并将忽略未安全派生的指针

因此,整个
指针的安全性
仅在您使用垃圾收集器时才相关。

问题不在于安全内存回收(SMR)是关于什么的。它询问了一个典型的C++实现中的技术难点。