多线程二进制树算法

Multi-Threaded Binary Tree Algorithm

本文关键字:算法 二进制 多线程      更新时间:2023-10-16

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

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

您不需要锁定您访问的每个节点。你可以这样做。在即将执行插入操作时锁定节点。插入并解锁。如果另一个线程碰巧需要在同一点插入它,并且节点被锁定,那么它应该等待,然后再向下遍历。一旦节点被解锁,它就可以继续遍历树的更新部分。

另一种简单的方法是为整个树设置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
}

锁定整个树。没有其他方法不会让你迟早陷入麻烦。当然,如果有大量的并发读写,你会遇到大量的阻塞,并使一切都慢下来。

Java在1.6版本中引入了一个并发跳过列表。跳过列表像树一样工作,但(据说)有点慢。然而,它们基于单链表,因此理论上可以使用比较和交换在不锁定的情况下进行修改。这使得多线程性能卓越。

我在谷歌上搜索了"跳过列表"C++比较和交换,得到了一些有趣的信息,但没有C++代码。然而,Java是开源的,所以如果你足够绝望的话,你可以得到算法。Java类是:Java.util.concurrent.CurrentSkipListMap.