多线程二叉树算法

时间:2011-06-28 12:38:40

标签: c++ tree pthreads binary-tree

所以我尝试了一种锁定每个节点的方法,但这需要很多锁定和解锁......这当然需要相当多的开销。我想知道是否有人知道更有效的算法。这是我的第一次尝试:

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);
   }
}

4 个答案:

答案 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