C++ 广播不';不要与障碍物打交道
我正在尝试使用pthreads实现一个基本的工作线程池。 这种情况下,我需要一个固定数量的工人,他们在我的项目期间一直生活 我永远不需要给单个线程发信号,但所有线程同时发信号,这就是为什么我想做单个广播的原因 在主程序继续之前,我需要等待所有线程完成,因此我决定在每个工作线程中使用barrier\u wait 问题是,如果我的线程调用barrier\u wait,广播就不能工作 下面是完整的示例和可编译代码。这只是针对广播的单触发器,在我的完整版本中,我将对如下内容进行因果循环C++ 广播不';不要与障碍物打交道,c++,c,multithreading,pthreads,parallel-processing,C++,C,Multithreading,Pthreads,Parallel Processing,我正在尝试使用pthreads实现一个基本的工作线程池。 这种情况下,我需要一个固定数量的工人,他们在我的项目期间一直生活 我永远不需要给单个线程发信号,但所有线程同时发信号,这就是为什么我想做单个广播的原因 在主程序继续之前,我需要等待所有线程完成,因此我决定在每个工作线程中使用barrier\u wait 问题是,如果我的线程调用barrier\u wait,广播就不能工作 下面是完整的示例和可编译代码。这只是针对广播的单触发器,在我的完整版本中,我将对如下内容进行因果循环 while(co
while(conditionMet){
1.prepare data
2.signal threads using data
3.post processing of thread results (because of barrier all threads finished)
4.modify conditionMet if needed
}
谢谢
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
void checkResults(char *str,int i){
fprintf(stdout,"%s:%d\n",str,i);
}
void checkResults(char *str,size_t n,int i){
fprintf(stdout,"%s[%lu]:%d\n",str,n,i);
}
/* For safe condition variable usage, must use a boolean predicate and */
/* a mutex with the condition. */
int conditionMet = 0;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_barrier_t barr;
#define NTHREADS 3
void *threadfunc(void *parm)
{
size_t i = (size_t) parm;
int rc;
rc = pthread_mutex_lock(&mutex);
checkResults("\tpthread_mutex_lock()",i, rc);
while (0==conditionMet) {
printf("\tThread blocked[%d]\n",(int)i);
rc = pthread_cond_wait(&cond, &mutex);
checkResults("\tpthread_cond_wait()",i, rc);
checkResults("\tbefore barrier",i);
rc = pthread_barrier_wait(&barr);//broadcast works if this is commented out
if(rc)
fprintf(stdout,"problems waiting for baarr\n");
checkResults("\tafter barrier",i);
}
rc = pthread_mutex_unlock(&mutex);
checkResults("\tpthread_mutex_lock()",i, rc);
return NULL;
}
int main(int argc, char **argv)
{
int rc=0;
int i;
pthread_t threadid[NTHREADS];
if(pthread_barrier_init(&barr, NULL,NTHREADS))
{
printf("Could not create a barrier\n");
}
printf("Enter Testcase - %s\n", argv[0]);
printf("Create %d threads\n", NTHREADS);
for(i=0; i<NTHREADS; ++i) {
rc = pthread_create(&threadid[i], NULL, threadfunc,(void *) i);
if(rc)
checkResults("pthread_create()", rc);
}
sleep(5); /* Sleep isn't a very robust way to serialize threads */
rc = pthread_mutex_lock(&mutex);
checkResults("pthread_mutex_lock()", rc);
/* The condition has occured. Set the flag and wake up any waiters */
conditionMet = 1;
printf("\nWake up all waiters...\n");
rc = pthread_cond_broadcast(&cond);
checkResults("pthread_cond_broadcast()", rc);
rc = pthread_mutex_unlock(&mutex);
checkResults("pthread_mutex_unlock()", rc);
printf("Wait for threads and cleanup\n");
for (i=0; i<NTHREADS; ++i) {
rc = pthread_join(threadid[i], NULL);
checkResults("pthread_join()", rc);
}
pthread_cond_destroy(&cond);
pthread_mutex_destroy(&mutex);
printf("Main completed\n");
return 0;
}
#包括
#包括
#包括
无效检查结果(字符*str,整数i){
fprintf(stdout,“%s:%d\n”,str,i);
}
无效检查结果(字符*str,大小\u t n,整数i){
fprintf(stdout,“%s[%lu]:%d\n”,str,n,i);
}
/*为了安全使用条件变量,必须使用布尔谓词和*/
/*具有该条件的互斥体*/
int conditionMet=0;
pthread_cond_t cond=pthread_cond_初始值设定项;
pthread\u mutex\u t mutex=pthread\u mutex\u初始值设定项;
pthread_barr_t barr;
#定义第3行
void*threadfunc(void*parm)
{
大小i=(大小)parm;
int rc;
rc=pthread\u mutex\u lock(&mutex);
检查结果(“\tpthread\u mutex\u lock()”,i,rc);
while(0==条件满足){
printf(“\t线程阻塞[%d]\n”,(int)i);
rc=pthread\u cond\u wait(&cond,&mutex);
检查结果(“\tpthread\u cond\u wait()”,i,rc);
检查结果(“\t屏障前”,i);
rc=pthread\u barrier\u wait(&barr);//如果将其注释掉,则广播工作
if(rc)
fprintf(标准输出,“等待baarr的问题”);
检查结果(“\t屏障后”,i);
}
rc=pthread\u mutex\u unlock(&mutex);
检查结果(“\tpthread\u mutex\u lock()”,i,rc);
返回NULL;
}
int main(int argc,字符**argv)
{
int rc=0;
int i;
pthread_t threadid[NTHREADS];
if(pthread\u barrier\u init(&barr,NULL,NTHREADS))
{
printf(“无法创建屏障\n”);
}
printf(“输入Testcase-%s\n”,argv[0]);
printf(“创建%d个线程\n”,n个线程);
对于(i=0;i线程函数将在接收到信号后立即锁定互斥体
。因此,只有一个线程函数将在屏障上等待(在互斥体
仍被锁定的情况下),屏障标准将永远无法满足
为了使用屏障,您应该重新设计应用程序的逻辑。互斥体必须在等待屏障之前立即解锁。pthread\u cond\u wait()
在代码中使用,应用程序中只有一个线程处于活动状态,这就完全消除了多线程的需要
编辑:
我想详细解释一下最后一句话。假设我们修改线程函数如下:
while (0==conditionMet) {
printf("\tThread blocked[%d]\n",(int)i);
rc = pthread_cond_wait(&cond, &mutex);
checkResults("\tpthread_cond_wait()",i, rc);
checkResults("\tbefore barrier",i);
pthread_mutex_unlock(&mutex); //added
rc = pthread_barrier_wait(&barr);//broadcast works if this is commented out
if(rc)
fprintf(stdout,"problems waiting for baarr\n");
checkResults("\tafter barrier",i);
}
void *threadfunc(void *parm)
{
/*...*/
struct ThreadRuntimeData {
} rtd;
while (0==conditionMet) {
printf("\tThread blocked[%d]\n",(int)i);
rc = pthread_cond_wait(&cond, &mutex);
checkResults("\tpthread_cond_wait()",i, rc);
GetWorkData(&rtd); //Gets some data from critical section and places it in rtd
pthread_mutex_unlock(&mutex);
ProcessingOfData(&rtd); //here we do the thread's job
//without the modification of global data; this may take a while
pthread_mutex_lock(&mutex);
PublishProcessedData(&rtd); //Here we modify some global data
//with the results of thread's work.
//Other threads may do the same, so we had to enter critical section again
pthread_mutex_unlock(&mutex);
checkResults("\tbefore barrier",i);
rc = pthread_barrier_wait(&barr);//broadcast works if this is commented out
if(rc)
fprintf(stdout,"problems waiting for baarr\n");
checkResults("\tafter barrier",i);
}
/*...*/
}
通过这种方式,我们可以在只有一个线程能够到达导致互斥锁
锁定的屏障时消除死锁。但在给定的时间内,仍然只有一个线程在关键部分运行:当它是pthread\u cond\u wait()时
返回被锁定的互斥锁,它将保持锁定状态,直到线程函数达到_unlock();_wait();对。只有在这之后,下一个线程才能运行并达到其屏障。清洗、冲洗、重复
OP prolly想要的是让线程函数同时运行(为什么其他人会想要线程池?)
while (0==conditionMet) {
printf("\tThread blocked[%d]\n",(int)i);
rc = pthread_cond_wait(&cond, &mutex);
checkResults("\tpthread_cond_wait()",i, rc);
checkResults("\tbefore barrier",i);
pthread_mutex_unlock(&mutex); //added
rc = pthread_barrier_wait(&barr);//broadcast works if this is commented out
if(rc)
fprintf(stdout,"problems waiting for baarr\n");
checkResults("\tafter barrier",i);
}
void *threadfunc(void *parm)
{
/*...*/
struct ThreadRuntimeData {
} rtd;
while (0==conditionMet) {
printf("\tThread blocked[%d]\n",(int)i);
rc = pthread_cond_wait(&cond, &mutex);
checkResults("\tpthread_cond_wait()",i, rc);
GetWorkData(&rtd); //Gets some data from critical section and places it in rtd
pthread_mutex_unlock(&mutex);
ProcessingOfData(&rtd); //here we do the thread's job
//without the modification of global data; this may take a while
pthread_mutex_lock(&mutex);
PublishProcessedData(&rtd); //Here we modify some global data
//with the results of thread's work.
//Other threads may do the same, so we had to enter critical section again
pthread_mutex_unlock(&mutex);
checkResults("\tbefore barrier",i);
rc = pthread_barrier_wait(&barr);//broadcast works if this is commented out
if(rc)
fprintf(stdout,"problems waiting for baarr\n");
checkResults("\tafter barrier",i);
}
/*...*/
}
当然,这只是一个草图。线程函数的优化设计取决于OP希望线程做什么
作为旁注,检查pthread\u barrier\u wait()的代码
返回结果必须考虑PTHREAD\u BARRIER\u SERIAL\u THREAD
返回。另外,将conditionMet
声明为volatile
更安全,线程函数在接收到信号后会立即锁定互斥体。因此,只有一个线程函数会等待BARRIER(在互斥锁
仍然锁定的情况下)并且永远不会满足屏障标准
为了使用屏障,您应该重新设计应用程序的逻辑。互斥体必须在等待屏障之前立即解锁。pthread\u cond\u wait()
在代码中使用,应用程序中只有一个线程处于活动状态,这就完全消除了多线程的需要
编辑:
我想详细解释一下最后一句话。假设我们修改线程函数如下:
while (0==conditionMet) {
printf("\tThread blocked[%d]\n",(int)i);
rc = pthread_cond_wait(&cond, &mutex);
checkResults("\tpthread_cond_wait()",i, rc);
checkResults("\tbefore barrier",i);
pthread_mutex_unlock(&mutex); //added
rc = pthread_barrier_wait(&barr);//broadcast works if this is commented out
if(rc)
fprintf(stdout,"problems waiting for baarr\n");
checkResults("\tafter barrier",i);
}
void *threadfunc(void *parm)
{
/*...*/
struct ThreadRuntimeData {
} rtd;
while (0==conditionMet) {
printf("\tThread blocked[%d]\n",(int)i);
rc = pthread_cond_wait(&cond, &mutex);
checkResults("\tpthread_cond_wait()",i, rc);
GetWorkData(&rtd); //Gets some data from critical section and places it in rtd
pthread_mutex_unlock(&mutex);
ProcessingOfData(&rtd); //here we do the thread's job
//without the modification of global data; this may take a while
pthread_mutex_lock(&mutex);
PublishProcessedData(&rtd); //Here we modify some global data
//with the results of thread's work.
//Other threads may do the same, so we had to enter critical section again
pthread_mutex_unlock(&mutex);
checkResults("\tbefore barrier",i);
rc = pthread_barrier_wait(&barr);//broadcast works if this is commented out
if(rc)
fprintf(stdout,"problems waiting for baarr\n");
checkResults("\tafter barrier",i);
}
/*...*/
}
通过这种方式,我们可以在只有一个线程能够到达导致互斥锁
锁定的屏障时消除死锁。但在给定的时间内,仍然只有一个线程在关键部分运行:当它是pthread\u cond\u wait()时
返回被锁定的互斥锁,它将保持锁定状态,直到线程函数达到_unlock();_wait();对。只有在这之后,下一个线程才能运行并达到其屏障。清洗、冲洗、重复
OP prolly想要的是让线程函数同时运行(为什么其他人会想要线程池?)
while (0==conditionMet) {
printf("\tThread blocked[%d]\n",(int)i);
rc = pthread_cond_wait(&cond, &mutex);
checkResults("\tpthread_cond_wait()",i, rc);
checkResults("\tbefore barrier",i);
pthread_mutex_unlock(&mutex); //added
rc = pthread_barrier_wait(&barr);//broadcast works if this is commented out
if(rc)
fprintf(stdout,"problems waiting for baarr\n");
checkResults("\tafter barrier",i);
}
void *threadfunc(void *parm)
{
/*...*/
struct ThreadRuntimeData {
} rtd;
while (0==conditionMet) {
printf("\tThread blocked[%d]\n",(int)i);
rc = pthread_cond_wait(&cond, &mutex);
checkResults("\tpthread_cond_wait()",i, rc);
GetWorkData(&rtd); //Gets some data from critical section and places it in rtd
pthread_mutex_unlock(&mutex);
ProcessingOfData(&rtd); //here we do the thread's job
//without the modification of global data; this may take a while
pthread_mutex_lock(&mutex);
PublishProcessedData(&rtd); //Here we modify some global data
//with the results of thread's work.
//Other threads may do the same, so we had to enter critical section again
pthread_mutex_unlock(&mutex);
checkResults("\tbefore barrier",i);
rc = pthread_barrier_wait(&barr);//broadcast works if this is commented out
if(rc)
fprintf(stdout,"problems waiting for baarr\n");
checkResults("\tafter barrier",i);
}
/*...*/
}
当然,这只是一个草图。线程函数的优化设计取决于OP希望线程做什么
作为旁注,检查pthread\u barrier\u wait()的代码
返回结果必须考虑PTHREAD\u BARRIER\u SERIAL\u THREAD
返回。另外,将conditionMet
声明为volatile
更安全,因为问题不清楚输入数据是什么,以及它们与线程和结果的关系如何.我看不出发布的代码,因为我看不出实际工作应该在哪里完成
<