广播不利于障碍

时间:2011-12-13 01:12:43

标签: c++ c multithreading pthreads parallel-processing

我正在尝试使用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;
}

3 个答案:

答案 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 结果:你没有真的需要一个障碍,你可以这样做:

  • 主线程
    1. 在输入队列
    2. 上推送 N 任务
    3. 等到您在输出队列
    4. 上收到 N 结果
  • 工作线程
    1. 从输入队列中弹出一个任务
    2. 计算结果
    3. 将结果推送到输出队列

最简单的同步队列只能一次推送/弹出一个项目(如果队列为空则推送信号,如果队列为空则弹出等待,等等)。

您可以轻松添加广播等push_n(vector<task> const &input)和等待所有pop_n(int count, vector<result> &output)结果的count等内容作为优化,但基本模式相同。

答案 2 :(得分:0)

你自己比自己更难做事。摆脱障碍。

如果您想等到所有工作完成,只需记住仍有待完成的工作数量。用互斥锁保护它。用条件变量触发它。然后使用pthread_cond_wait等待它达到零。 (您可以使用已用于处理作业队列的相同逻辑。)

或者,将线程编码为在没有更多工作要做时终止。然后等待,直到所有工作线程都以pthread_join终止。