我正在尝试使用pthreads实现基本工作池。 这个场景是我想要一个固定数量的工人,这些工人在整个计划期间都会存在。
我永远不需要发信号通知单线程,但是所有线程都是一次性的,这就是为什么我要做一次广播。
我需要在主程序继续之前等待所有线程完成,所以我决定在每个工作线程中使用barrier_wait。
问题是,如果我的线程调用barrier_wait,则广播不起作用。
完整示例和可编译代码如下所示。这只是针对广播的单个触发器,在我的完整版本中,我将导致像
这样的循环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;
}
答案 0 :(得分:3)
线程函数将在收到信号后立即锁定mutex
。因此,只有一个线程函数会在屏障上等待(mutex
仍处于锁定状态)并且永远不会满足屏障标准。
您应该重新设计应用程序的逻辑以使用屏障。必须在等待障碍之前解锁mutex
。此外,鉴于代码中pthread_cond_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);
}
这样我们可以在只有一个线程能够达到mutex
锁定的屏障时消除死锁。但是在给定的时间内只剩下一个线程将在临界区运行:当pthread_cond_wait()
返回时,mutex
被锁定并且它将一直保持锁定,直到线程函数到达_unlock(); _等待();对。只有在那之后,下一个单线程才能运行并达到其障碍。洗涤,冲洗,重复......
OP最想要的是让线程函数同时运行(为什么还有人想拥有一个线程池?)。在这种情况下,函数可能如下所示:
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_barrier_wait()
返回结果的代码必须考虑PTHREAD_BARRIER_SERIAL_THREAD
返回。将conditionMet
声明为volatile
也是更安全的。
答案 1 :(得分:1)
从问题中不清楚输入数据是什么,以及它们与线程和结果的关系。我无法从发布的代码中看出,因为我无法看到实际工作应该在哪里完成。
假设你有 N (子)任务, N 线程,并希望主线程等待 N 结果:你没有真的需要一个障碍,你可以这样做:
最简单的同步队列只能一次推送/弹出一个项目(如果队列为空则推送信号,如果队列为空则弹出等待,等等)。
您可以轻松添加广播等push_n(vector<task> const &input)
和等待所有pop_n(int count, vector<result> &output)
结果的count
等内容作为优化,但基本模式相同。
答案 2 :(得分:0)
你自己比自己更难做事。摆脱障碍。
如果您想等到所有工作完成,只需记住仍有待完成的工作数量。用互斥锁保护它。用条件变量触发它。然后使用pthread_cond_wait
等待它达到零。 (您可以使用已用于处理作业队列的相同逻辑。)
或者,将线程编码为在没有更多工作要做时终止。然后等待,直到所有工作线程都以pthread_join
终止。