多核处理器上的 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(>,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 问题的主要内容,如果未能解决你的问题,请参考以下文章