所以我尝试了一种锁定每个节点的方法,但这需要很多锁定和解锁......这当然需要相当多的开销。我想知道是否有人知道更有效的算法。这是我的第一次尝试:
typedef struct _treenode{
struct _treenode *leftNode;
struct _treenode *rightNode;
int32_t data;
pthread_mutex_t mutex;
}TreeNode;
pthread_mutex_t _initMutex = PTHREAD_MUTEX_INITIALIZER;
int32_t insertNode(TreeNode **_trunk, int32_t data){
TreeNode **current;
pthread_mutex_t *parentMutex = NULL, *currentMutex = &_initMutex;
if(_trunk != NULL){
current = _trunk;
while(*current != NULL){
pthread_mutex_lock(&(*current)->mutex);
currentMutex = &(*current)->mutex;
if((*current)->data < data){
if(parentMutex != NULL)
pthread_mutex_unlock(parentMutex);
pthreadMutex = currentMutex;
current = &(*current)->rightNode;
}else if((*current)->data > data){
if(parentMutex != NULL)
pthread_mutex_unlock(parentMutex);
parentMutex = currentMutex;
current = &(*current)->leftNode;
}else{
pthread_mutex_unlock(currentMutex);
if(parentMutex != NULL)
pthread_mutex_unlock(parentMutex);
return 0;
}
}
*current = malloc(sizeof(TreeNode));
pthread_mutex_init(&(*current)->mutex, NULL);
pthread_mutex_lock(&(*current)->mutex);
(*current)->leftNode = NULL;
(*current)->rightNode = NULL;
(*current)->data = data;
pthread_mutex_unlock(&(*current)->mutex);
pthread_mutex_unlock(currentMutex);
}else{
return 1;
}
return 0;
}
int main(){
int i;
TreeNode *trunk = NULL;
for(i=0; i<1000000; i++){
insertNode(&trunk, rand() % 50000);
}
}
答案 0 :(得分:1)
您无需锁定所访问的每个节点。你可以做这样的事情。当您即将进行插入时锁定节点。你的插入和解锁。如果另一个线程碰巧需要在同一点插入它并且节点被锁定,它应该在进一步向下移动之前等待。一旦节点解锁,它就可以继续遍历树的更新部分。
答案 1 :(得分:1)
另一种直截了当的方法是对整个树进行1次锁定。
您对树有更多的序列化访问权限,但您只有一个互斥锁且只能锁定一次。 如果序列化是个问题,请使用read/write lock。所以至少可以并行阅读。
答案 2 :(得分:1)
使用读写锁。如果您稍后决定切换树实现,则锁定单个节点将变得异常困难。这是使用pthreads的一个小的演示代码:
typedef struct {
pthread_rwlock_t rwlock;
TreeNode *root_node;
} Tree;
void Tree_init(Tree *tree) {
pthread_rwlock_init(&tree->rwlock, NULL);
tree->root_node = NULL;
}
int32_t Tree_insert(Tree *tree, int32_t data) {
pthread_rwlock_wrlock(&tree->rwlock);
int32_t ret = _insertNode(&tree->root_node, data);
pthread_rwlock_unlock(&tree->rwlock);
return ret;
}
int32_t Tree_locate(Tree *tree) {
pthread_rwlock_rdlock(&tree->rwlock);
int32_t ret = _locateNode(&tree->root_node);
pthread_rwlock_unlock(&tree->rwlock);
return ret;
}
void Tree_destroy(Tree *tree) {
pthread_rwlock_destroy(&tree->rwlock);
// yada yada
}
答案 3 :(得分:1)
锁定整棵树。没有别的方法迟早会让你陷入麻烦之中。当然,如果有很多并发读写操作,你会得到很多阻塞,并且可以放慢速度。
Java在1.6版中引入了并发跳过列表。跳过列表就像树一样工作,但是(据说)有点慢。但是,它们基于单链表,因此理论上可以在不使用比较和交换锁定的情况下进行修改。这样可以实现卓越的多线程性能。
我用Google搜索“跳过列表”C ++比较和交换,并提出了一些有趣的信息,但没有C ++代码。但是,Java是开源的,所以如果你非常绝望,你可以获得算法。 Java类是: java.util.concurrent.ConcurrentSkipListMap 。