多核处理器上的 pthread_cond_wait 问题

Posted

技术标签:

【中文标题】多核处理器上的 pthread_cond_wait 问题【英文标题】:Problem with pthread_cond_wait on multi-core processors 【发布时间】:2021-11-09 00:40:13 【问题描述】:

我正在编写一个程序,它从 websocket 接收数据并在线程池中处理这些数据。 当处理器有 2 个或更多内核时,我对 pthread_cond_wait 有问题。在不同内核上运行的所有线程接收到 pthread_cond_signal 信号后。例如,如果我有 2 个内核,那么信号将一次到达 2 个线程,它们位于这两个内核上。如果我有单核处理器,一切都很好。 我必须做什么才能让程序在多核处理器上正常工作?这样只有一个线程接收到开始工作的信号。 我用生成随机文本数据而不是 websocket 数据编写了我的代码示例。

#include<stdio.h>
#include<stdlib.h>
#include<cstring>
#include<pthread.h>
#include<unistd.h>

pthread_attr_t attrd;
pthread_mutex_t mutexQueue;
pthread_cond_t condQueue;

char textArr[128][24]; //array with random text to work
int tc; //tasks count
int gi; //global array index

void *workThread(void *args)
 int ai;//internal index for working array element 
 while(1)
  pthread_mutex_lock(&mutexQueue);
  while(tc==0)
   pthread_cond_wait(&condQueue,&mutexQueue); //wait for signal if tasks count = 0.
  
  ai=gi;
  if(gi==127)gi=0;else gi++;
  tc--;
  pthread_mutex_unlock(&mutexQueue);
  printf("%s\r\n",textArr[ai]);
  // then work with websocket data
 


void *generalThread(void *args)
 const char chrs[]="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; //chars fo random text generation
 int ai=0;
 srand(time(NULL));
 while(1)
  for(int i=0;i<23;i++)textArr[ai][i]=chrs[rand()%61];//generating data instead of websocket data 
  textArr[ai][23]='\0';
  tc++;
  pthread_cond_signal(&condQueue); //Send signal for thread to begin work with data
  if(ai==127)ai=0;else ai++;
 


int main(int argc,char *argv[])
 pthread_attr_init(&attrd);
 pthread_attr_setdetachstate(&attrd,PTHREAD_CREATE_DETACHED);
 pthread_t gt,wt[32];
 for(int i=0;i<32;i++)pthread_create(&wt[i],&attrd,&workThread,NULL);
 pthread_create(&gt,NULL,&generalThread,NULL);
 pthread_join(gt,NULL);
 return 0;

【问题讨论】:

generalThread 有两个问题。首先,它应该在更新tc 和调用pthread_cond_signal 时锁定互斥锁。其次,当循环缓冲区填满时,它应该sleep。按照现在的代码,generalThread 可以比工作人员删除字符串更快地向缓冲区添加字符串。 @user3386109 谢谢。但是如果我检查 tc!=0 两个线程都将返回 true,因为它们工作相同。 @user3386109 当 websocket 工作时,缓冲区填满的速度相当慢。不需要睡觉。一般来说,互斥锁如何帮助线程立即接收信号? @BadMan 在写下我现在已删除的评论后,我注意到您正在检查tc!=0,因为while (tc==0) 循环。所以已经编写了代码来处理虚假唤醒。如果两个线程唤醒,只有一个线程应该能够获取互斥锁。所以只有一个线程应该看到tc!=0。您可以通过在每次调用 pthread_cond_signal 后在 generalThread 中调用 sleep 来验证这一点。 代码中似乎缺少的另一件事是initialization of the mutex and condition variable。 【参考方案1】:

tc++ 添加互斥锁完全纠正了我的程序:

void *generalThread(void *args) 
   const char chrs[]="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
   int ai=0;
   srand(time(NULL));
   while(1)
      for(int i=0;i<23;i++)textArr[ai][i]=chrs[rand()%61];
      textArr[ai][23]='\0';
      pthread_mutex_lock(&mutexQueue); //this has been added 
      tc++;
      pthread_mutex_unlock(&mutexQueue); //this has been added 
      pthread_cond_signal(&condQueue);
      if(ai==127)ai=0;else ai++;
   

【讨论】:

【参考方案2】:

首先是一些信息:

男人 pthread_cond_wait

基本原理

当条件变量在不同的处理器上同时发出信号时,某些实现,尤其是在多处理器上,有时可能会导致多个线程被唤醒。

男人 pthread_cond_signal

基本原理

条件信号的多次唤醒

在多处理器上,pthread_cond_signal() 的实现可能无法避免解除对一个条件变量上阻塞的多个线程的阻塞。

...

效果是,由于一次调用 pthread_cond_signal(),多个线程可以从其对 pthread_cond_wait() 或 pthread_cond_timedwait() 的调用中返回。这种效应称为“虚假唤醒”。请注意,这种情况是自我纠正的,因为如此唤醒的线程数是有限的;例如,在块上的事件序列之后调用 pthread_cond_wait() 的下一个线程。

到目前为止,一切顺利,您的workThread 中的代码已正确同步(但您也应该将printf 放在同步部分),但您的generalThread 中的代码根本没有同步。用锁/解锁来封装while循环中的代码。

在这种情况下,第一个被唤醒的线程必须获取指定互斥锁上的锁,该互斥锁将由另一个线程或generalThread 拥有。在互斥锁被解锁之前,线程会阻塞(不管它的唤醒原因是什么)。获取后,它拥有互斥锁,所有其他线程将被阻塞,包括generalThread

注意pthread_cond_wait 在进入等待状态时隐式解锁指定的互斥锁,并在唤醒时尝试获取指定互斥锁上的锁。

【讨论】:

谢谢!据我了解,我必须锁定 tc++;一般线程 每个线程访问和修改的所有内容。 看来,gi 是唯一的保存变量(仅在workThread 中出现)。

以上是关于多核处理器上的 pthread_cond_wait 问题的主要内容,如果未能解决你的问题,请参考以下文章

网络分流器-网络分流器-多核编程的几个难题及其应对策略

在信号句柄中使用 pthread_cond_wait 的 Pthread 被阻塞

Java7 Fork-Join 框架:任务切分,并行处理

多核 CPU 和多个 CPU 有何区别

pthread_cond_wait() 同时唤醒两个线程

多核 CPU 上的 Redis 性能