Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.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++ std::shared#ptr';让我们使用_count()使其成为可靠的计数器?_C++_Multithreading_Shared Ptr_Memory Barriers_Reference Counting - Fatal编程技术网

C++ std::shared#ptr';让我们使用_count()使其成为可靠的计数器?

C++ std::shared#ptr';让我们使用_count()使其成为可靠的计数器?,c++,multithreading,shared-ptr,memory-barriers,reference-counting,C++,Multithreading,Shared Ptr,Memory Barriers,Reference Counting,我正在实现一个线程安全的“lazy synchronized”集,它是由共享的ptr连接的节点的链接列表。该算法来自“多处理器编程的艺术”。我正在添加一个is\u empty()函数,该函数需要与现有函数线性化:contains()、add()、remove()。在下面的代码中,您可以看到remove是一个两步过程。首先,它通过设置marked=nullptr“lazy”标记节点,然后它物理地移动链表next指针 修改类以支持is_empty() 模板 类懒散集:公共集{ 公众: 懒散(); 布

我正在实现一个线程安全的“lazy synchronized”集,它是由共享的ptr连接的节点的链接列表。该算法来自“多处理器编程的艺术”。我正在添加一个
is\u empty()
函数,该函数需要与现有函数线性化:
contains()、add()、remove()
。在下面的代码中,您可以看到
remove
是一个两步过程。首先,它通过设置
marked=nullptr
“lazy”标记节点,然后它物理地移动链表
next
指针

修改类以支持is_empty()

模板
类懒散集:公共集{
公众:
懒散();
布尔包含(常数T&)常数;
布尔为空()常量;
布尔加(常数T&);
bool-remove(const-T&);
私人:
bool验证(const std::shared_ptr&,const std::shared_ptr&);
类节点;
std::共享头;
std::shared_ptr counter;//注意:类型不重要,永远不会更改为true/fase
};
模板
类LazySet::Node{
公众:
节点();
节点(常数T&);
T键;
std::shared_ptr marked;//假设初始化为=LazySet.counter
//nullptr表示已标记;否则为未标记
std::共享\u ptr next;
std::互斥mtx;
};
支持的相关修改方法为空

模板
bool LazySet::remove(常量T&k){
std::共享的ptr pred;
std::共享\u ptr curr;
while(true){
pred=头部;
curr=原子负载(&(head->next));
//查找键应位于排序列表中的窗口
而((当前)和((当前->键next));
}
//阿奎尔锁在窗口上,从左到右锁防止死锁
(pred->mtx).lock();
if(curr){//仅当不为null时锁定ptr
(curr->mtx).lock();
}
//确保车窗在锁定前未发生变化,然后将其拆下
如果(验证(前、当前)){
如果(!curr){//key不存在,则不执行任何操作
//##无关紧要##
}否则{//密钥存在,请将其删除
原子存储(&(curr->marked),nullptr);//逻辑“惰性”删除
原子存储(&(pred->next),curr->next)//物理删除
(当前->mtx).unlock();
(pred->mtx).unlock();
返回true;
}
}否则{
//##解锁并再次循环##
}
}
}
模板
bool LazySet::contains(const T&k)const{
std::共享\u ptr curr;
curr=原子负载(&(head->next));
//查找键应位于排序列表中的窗口
而((当前)和((当前->键next));
}
//检查窗口中是否存在密钥
如果(当前){
如果(curr->key==k){//key存在,除非标记
返回(原子负载(&(curr->marked))!=nullptr;
}否则{//不存在
返回false;
}
}否则{//不存在
返回false;
}
}
Node.marked
最初是一个普通bool,
LazySet.counter
不存在。让它们共享的选择是能够原子地修改节点数量上的计数器和节点上的延迟删除标记。在
remove()
中同时修改这两者是必要的,这样
是空的()
才能与
contains()
线性化。(如果没有双宽CA之类的东西,它不可能是一个单独的布尔标记和int计数器。)我希望使用共享的\u ptr的
use\u count()
函数实现计数器,但在多线程上下文中,由于
内存顺序松弛,它只是一个近似值

我知道独立围栏通常是不好的做法,我不太熟悉使用它们但是如果我实现的
是空的
如下所示,围栏会确保它不再是近似值,而是可靠计数器的精确值吗?

模板
bool LazySet::is_empty()const{
//##一些完整的内存障碍
if(计数器使用_count()==1){
//##一些完整的内存障碍
返回真值
}
//##一些完整的内存障碍
返回错误
}
我这样问只是因为他说:

如果不增加更多的围栏,我们就无法使
使用\u count()
可靠


放松记忆顺序不是这里的问题<代码>使用计数
不“可靠”,因为在返回值时,该值可能已更改。在获取值本身时没有数据竞争,但是在基于该值的任何条件语句之前,没有任何东西可以阻止该值被修改

因此,如果依赖于它的值仍然是有意义的,则无法对其执行任何操作(例外情况是,如果您仍然持有一个
shared_ptr
实例,那么使用计数将不会变为0)。使其可靠的唯一方法是防止其被更改。所以你需要一个互斥体

而且互斥锁必须锁定,不仅是在
use\u count
调用和使用时,而且每次您分发一个
shared\u ptr
时,您都会从中获得
use\u count

// ## SOME FULL MEMORY BARRIER
if (counter.use_count() == 1) {
    // ## SOME FULL MEMORY BARRIER
使用之前的acquire fence,您可以确保“看到”其他线程中所有所有者的所有重置(包括在分配和销毁期间)的结果。acquire围栏提供了所有以下轻松操作acquire语义,防止它们“在将来获取值”(这在任何情况下都是语义上的疯狂,可能会使所有程序都正式成为UB)


(打电话后没有什么有意义的围栏。)

对于我们这些没有围栏的人,你能扩展一下“懒惰”的含义吗