Trie结构,无锁插入
Trie structure, lock-free inserting
我尝试实现无锁Trie结构,但我卡在了插入节点上。起初我认为这很容易(我的 trie 结构不会有任何删除方法)但即使以原子方式交换一个指针也可能很棘手。
我想仅在它为 nullptr 时以原子方式交换指向结构(TrieNode)的指针,以确保我不会丢失其他线程可以插入其间的其他点头。
struct TrieNode{
int t =0;
std::shared_ptr<TrieNode> child{nullptr};
};
std::shared_ptr<TrieNode> root;
auto p = std::atomic_load(&root);
auto node = std::make_shared<TrieNode>();
node->t=1;
auto tmp = std::shared_ptr<TrieNode>{nullptr};
std::cout<<std::atomic_compare_exchange_strong( &(p->child), &tmp,node)<<std::endl;
std::cout<<node->t;
使用此代码我得到退出代码 -1073741819 (0xC0000005)。
编辑:感谢您的所有评论。也许我没有说明我的问题,所以我想解决它 now.After 昨天大约 10 个小时的编码我改变了一些东西。现在我使用普通指针,现在它正在工作。我现在没有测试它是否在多个线程插入单词的情况下自由竞争。我打算今天做。
const int ALPHABET_SIZE =4;
enum Alphabet {A,T,G,C,END};
class LFTrie{
private:
struct TrieNode{
std::atomic<TrieNode*> children[ALPHABET_SIZE+1];
};
std::atomic<TrieNode*> root = new TrieNode();
public:
void Insert(std::string word){
auto p =root.load();
int index;
for(int i=0; i<=word.size();i++){
if(i==word.size())
index = END;
else
index = WhatIndex(word[i]);
auto expected = p->children[index].load();
if(!expected){
auto node = new TrieNode();
if(! p->children[index].compare_exchange_strong(expected,node))
delete node;
}
p = p->children[index];
}
}
};
现在我相信它可以处理许多线程插入不同的词。是的,在这个解决方案中,如果下一个指针不为空,我将丢弃节点。抱歉给您带来麻烦(我不是母语人士)。
CAS 模式应该是这样的:
auto expected = p->child;
while( !expected ){
if (success at CAS(&p->child, &expected, make_null_replace() ))
break;
}
如果您不注意 return value/expected 并测试您正在替换本地存储的 null,那您就有麻烦了。
失败时,您需要丢弃您创建的新节点。
我尝试实现无锁Trie结构,但我卡在了插入节点上。起初我认为这很容易(我的 trie 结构不会有任何删除方法)但即使以原子方式交换一个指针也可能很棘手。 我想仅在它为 nullptr 时以原子方式交换指向结构(TrieNode)的指针,以确保我不会丢失其他线程可以插入其间的其他点头。
struct TrieNode{
int t =0;
std::shared_ptr<TrieNode> child{nullptr};
};
std::shared_ptr<TrieNode> root;
auto p = std::atomic_load(&root);
auto node = std::make_shared<TrieNode>();
node->t=1;
auto tmp = std::shared_ptr<TrieNode>{nullptr};
std::cout<<std::atomic_compare_exchange_strong( &(p->child), &tmp,node)<<std::endl;
std::cout<<node->t;
使用此代码我得到退出代码 -1073741819 (0xC0000005)。
编辑:感谢您的所有评论。也许我没有说明我的问题,所以我想解决它 now.After 昨天大约 10 个小时的编码我改变了一些东西。现在我使用普通指针,现在它正在工作。我现在没有测试它是否在多个线程插入单词的情况下自由竞争。我打算今天做。
const int ALPHABET_SIZE =4;
enum Alphabet {A,T,G,C,END};
class LFTrie{
private:
struct TrieNode{
std::atomic<TrieNode*> children[ALPHABET_SIZE+1];
};
std::atomic<TrieNode*> root = new TrieNode();
public:
void Insert(std::string word){
auto p =root.load();
int index;
for(int i=0; i<=word.size();i++){
if(i==word.size())
index = END;
else
index = WhatIndex(word[i]);
auto expected = p->children[index].load();
if(!expected){
auto node = new TrieNode();
if(! p->children[index].compare_exchange_strong(expected,node))
delete node;
}
p = p->children[index];
}
}
};
现在我相信它可以处理许多线程插入不同的词。是的,在这个解决方案中,如果下一个指针不为空,我将丢弃节点。抱歉给您带来麻烦(我不是母语人士)。
CAS 模式应该是这样的:
auto expected = p->child;
while( !expected ){
if (success at CAS(&p->child, &expected, make_null_replace() ))
break;
}
如果您不注意 return value/expected 并测试您正在替换本地存储的 null,那您就有麻烦了。
失败时,您需要丢弃您创建的新节点。