pthread_mutex_lock / unlock的性能

| 我已经注意到,当我拥有一种可以锁定和解锁线程的算法时,性能会受到很大的影响。 有什么办法可以帮助解决这一开销?使用信号量会效率更高/更低吗? 谢谢
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);
   }
}
    
已邀请:
        不用担心草叶,而后退并观察整个森林。 任何依赖于两个线程(可能彼此紧密地踩在彼此的脚趾上)的算法本质上效率低下。尝试找到一种方法来大大减少交互的需求。 例如,如果一个线程产生数据而另一个线程消耗数据,则一个线程可以轻松地想出一种效率低下的算法,其中生产者将数据发布到共享内存中,然后等待另一个线程消耗它。同时,消费者正在等待生产者完成,等等。通过将生产者写入文件或管道,然后消费者从文件或管道中读取,这一切都大大简化了。     
        
pthread_mutex_lock
pthread_mutex_unlock
的成本视竞争而有所不同: 单线程使用-仅存在一个线程,或者仅一个线程正在使用互斥锁及其保护的资源:锁定实际上是免费的,最多最多80-100个周期。 多个线程使用了资源,但是锁的持有时间间隔很短,而且争用很少发生:锁有一定的成本,并且很难衡量。成本主要包括使其他“ / cpus”缓存行无效。 显着的锁争用:几乎每个锁和解锁操作都需要内核的帮助,并且每个锁/解锁的成本很容易达到数千(可能甚至数万)个周期。 尽管如此,在大多数情况下和大多数实现中,互斥锁应该是最便宜的锁定原语。有时自旋锁的性能可能更好。我永远都不会指望信号量会更好。     
        据我所知,您的锁策略不是最佳的,因为大多数锁不会用于更改数据,而只是用于读取并找到通过树的方式。
pthread_rwlock_t
可以帮上忙。您只需要在树中向下的路径上进行读锁定,直到找到要进行某些修改的节点为止。然后,您将在那里执行写锁定。这样一来,当您在其他分支中的树上行走时,其他线程可以执行相同的任务,而不会互相干扰。 ѭ3的一个体面的实现将为读者提供一个计数器,该计数器会随着原子操作而变化,只要与作者没有争执即可。这应该非常快。我认为,一旦发生争用,这将与互斥体一样昂贵。     
        您的锁可能太细了。当然,最佳粒度可以根据工作负荷而变化。 您可以对整个树使用单个锁,并且它的性能可能更好。但是,如果您进行大量读取并且插入/删除操作相对较少,那么最终您会无缘无故地锁定整棵树。您可能要使用读取器-写入器锁,这将允许同时使用多个读取器。 当链接列表的细粒度锁定和粗粒度锁定之间存在比较时,您的问题使我想起了另一个问题。在粗粒度版本中,每个线程轮流运行(不是并行运行),并且总运行时间略大于每个线程的运行时间总和,而在细粒度版本中,总运行时间要大得多少于每个线程的运行时间的总和,细粒度锁定的额外开销完全抵消了这些好处,从而使细粒度版本比粗粒度版本慢。     
        在pthread_mutex_lock / unlock的情况下,锁定和解锁是非常昂贵的操作。关于算法的更多细节,我可以提出一些建议,但据我所知,我不能肯定地告诉您任何信息。信号量是一种替代方法(再次取决于算法),并且障碍是并发的另一种有用方法。为了减少开销,您可以执行一些操作,例如使锁的粒度更小或更大。在循环中多次锁定锁是一个坏主意,您可能希望将其移出循环。这只是一个例子,但我想出的可能还更多。这是关于确定锁的开销是否大于代码关键部分的开销。如果您提供算法或一些示例代码,我将很高兴对其进行研究。     
pthread_mutex_lock和pthread_cond_wait是OS原语-它们将调用线程置于睡眠状态,将控制权转移到另一个线程。即它们涉及系统调用和大量开销。在两个线程之间的紧密集成中,您实际上并不想放弃任何控制甚至一个周期。 给出我建议使用
volatile int
变量而不是互斥体:
volatile int data_ready = 0;
/*  ... */
while (!data_ready);
process_data();
data_ready = 0;
    

要回复问题请先登录注册