C中的线程同步和条件变量问题

C中的线程同步和条件变量问题,c,multithreading,synchronization,pthreads,C,Multithreading,Synchronization,Pthreads,我有三个线程,一个是主线程,另外两个是辅助线程。当有工作要做时,第一个线程唤醒两个线程中的一个。每一个线程在被唤醒时都会执行一些计算,如果它发现有更多的工作要做,则可以唤醒另一个工作线程,或者干脆决定自己做这项工作(例如,通过将工作添加到本地队列)。 当工作线程有工作要做时,主线程必须等待工作完成。我用以下条件变量实现了这一点(这里报告的代码隐藏了很多细节,请询问是否有不可理解的地方): 主线程(伪代码): 工作线程: while (1){ pthread_mutex_lock(&

我有三个线程,一个是主线程,另外两个是辅助线程。当有工作要做时,第一个线程唤醒两个线程中的一个。每一个线程在被唤醒时都会执行一些计算,如果它发现有更多的工作要做,则可以唤醒另一个工作线程,或者干脆决定自己做这项工作(例如,通过将工作添加到本地队列)。 当工作线程有工作要做时,主线程必须等待工作完成。我用以下条件变量实现了这一点(这里报告的代码隐藏了很多细节,请询问是否有不可理解的地方):

主线程(伪代码):

工作线程:

while (1){

   pthread_mutex_lock(&main_lock);
    if (work == 0)
       pthread_cond_signal(&main_cond);
    pthread_mutex_unlock(&main_lock);  

    //code to let the worker thread wait again -- PROBLEM!

   while (I have work to do, in my queue){
       do_work()
   }

}
问题是:当一个工作线程唤醒主线程时,我不确定该工作线程是否调用了wait以使自己处于等待新工作的状态。即使我用另一个条件变量来实现这个等待,也可能发生主线程是唤醒的,在到达某个点之前会做一些工作,在该点上他必须唤醒尚未调用等待的线程。。。这可能会导致糟糕的结果。我已经尝试了几种方法来解决这个问题,但我找不到解决方案,也许有一种明显的方法可以解决它,但我错过了

你能提供一个解决这类问题的方案吗?我使用的是C语言,我可以使用您认为合适的任何同步机制,比如pthreads或posix信号量

谢谢

您能拥有由主线程管理的“新作业”队列吗?主线程一次可以向每个工作线程分发一个作业。主线程还将监听工人完成的作业。如果工作线程发现需要执行的新作业,只需将其添加到“新作业”队列中,主线程就会分发它

伪代码:

//this function can be called from the main several time. It blocks the main thread till the work is done.
void new_work(){

//signaling to worker threads if work is available

    //Now, the threads have been awakened, it's time to sleep till they have finished.
    pthread_mutex_lock(&main_lock);
    while (work > 0)    //work is a shared atomic integer, incremented each time there's work to do and decremented when finished executing some work unit
       pthread_cond_wait(&main_cond);
    pthread_mutex_unlock(&main_lock);

}
JobQueue NewJobs;
Job JobForWorker[NUM_WORKERS];

workerthread()
{
  while(wait for new job)
  {
    do job (this may include adding new jobs to NewJobs queue)
    signal job complete to main thread
  }
}

main thread()
{
  while(whatever)
  {
    wait for job completion on any worker thread
    now a worker thread is free put a new job on it
  }
}
您是否可以拥有由主线程管理的“新作业”队列?主线程一次可以向每个工作线程分发一个作业。主线程还将监听工人完成的作业。如果工作线程发现需要执行的新作业,只需将其添加到“新作业”队列中,主线程就会分发它

伪代码:

//this function can be called from the main several time. It blocks the main thread till the work is done.
void new_work(){

//signaling to worker threads if work is available

    //Now, the threads have been awakened, it's time to sleep till they have finished.
    pthread_mutex_lock(&main_lock);
    while (work > 0)    //work is a shared atomic integer, incremented each time there's work to do and decremented when finished executing some work unit
       pthread_cond_wait(&main_cond);
    pthread_mutex_unlock(&main_lock);

}
JobQueue NewJobs;
Job JobForWorker[NUM_WORKERS];

workerthread()
{
  while(wait for new job)
  {
    do job (this may include adding new jobs to NewJobs queue)
    signal job complete to main thread
  }
}

main thread()
{
  while(whatever)
  {
    wait for job completion on any worker thread
    now a worker thread is free put a new job on it
  }
}

我相信你在这里看到的是。您正在做的是编写一个计数信号量的特殊实现(一个用于提供不只是互斥的信号量)

如果我没有看错你的问题,你要做的是让工作线程阻塞,直到有一个工作单元可用,然后在它可用时执行一个工作单元。您的问题在于有太多的可用工作,而主线程试图取消阻止已在工作的工作线程。我将按如下方式构造您的代码

sem_t main_sem;
sem_init(&main_sem, 0, 0);

void new_work() {
    sem_post(&main_sem);
    pthread_cond_wait(&main_cond);
}

void do_work() {
    while (1) {
        sem_wait(&main_sem);
        // do stuff
        // do more stuff
        pthread_cond_signal(&main_sem);
    }
}
现在,如果工作线程生成更多的工作,那么它们可以简单地
sem\u post
到信号量,并简单地延迟
pthread\u cond\u信号
,直到所有工作完成


但是,请注意,如果您确实需要在辅助线程工作时始终阻止主线程,那么当您可以调用执行该工作的函数时,将该工作推送到另一个线程是没有用的。

我相信您在这里看到的是。您正在做的是编写一个计数信号量的特殊实现(一个用于提供不只是互斥的信号量)

如果我没有看错你的问题,你要做的是让工作线程阻塞,直到有一个工作单元可用,然后在它可用时执行一个工作单元。您的问题在于有太多的可用工作,而主线程试图取消阻止已在工作的工作线程。我将按如下方式构造您的代码

sem_t main_sem;
sem_init(&main_sem, 0, 0);

void new_work() {
    sem_post(&main_sem);
    pthread_cond_wait(&main_cond);
}

void do_work() {
    while (1) {
        sem_wait(&main_sem);
        // do stuff
        // do more stuff
        pthread_cond_signal(&main_sem);
    }
}
现在,如果工作线程生成更多的工作,那么它们可以简单地
sem\u post
到信号量,并简单地延迟
pthread\u cond\u信号
,直到所有工作完成


但是请注意,如果您确实需要在辅助线程工作时始终阻止主线程,那么当您可以只调用执行该工作的函数时,将该工作推送到另一个线程是没有用的。

如果您希望主线程将工作分配给其他两个线程,那么请等到两个线程都完成工作后再继续,你可以用一个屏障来完成这项工作

屏障是一种同步构造,您可以使用它使线程在代码中的某个点等待,直到设置数量的线程都准备好继续。本质上,您初始化一个pthread屏障,即在允许任何线程继续之前,必须有x个线程等待它。当每个线程完成其工作并准备继续时,它将在屏障上等待,一旦x个线程到达屏障,它们都可以继续

在您的情况下,您可能可以执行以下操作:

pthread_barrier_t barrier;
pthread_barrier_init(&barrier, 3);

master()
{
  while (work_to_do) {
    put_work_on_worker_queues();
    pthread_barrier_wait(&barrier);
  }
}

worker()
{
  while(1) {
    while (work_on_my_queue()) {
      do_work();
    }
    pthread_barrier_wait(&barrier);
  }
}

这将使主线程发出工作,然后等待两个工作线程完成它们所完成的工作(如果有的话),然后再继续。

如果希望主线程将工作分配给其他两个线程,然后等待两个线程完成其工作,然后再继续,您可以使用屏障来完成这项工作

屏障是一种同步构造,您可以使用它使线程在代码中的某个点等待,直到设置数量的线程都准备好继续。本质上,您初始化一个pthread屏障,即在允许任何线程继续之前,必须有x个线程等待它。当每个线程完成其工作并准备继续时,它将在屏障上等待,一旦x个线程到达屏障,它们都可以继续

在您的情况下,您可能可以执行以下操作:

pthread_barrier_t barrier;
pthread_barrier_init(&barrier, 3);

master()
{
  while (work_to_do) {
    put_work_on_worker_queues();
    pthread_barrier_wait(&barrier);
  }
}

worker()
{
  while(1) {
    while (work_on_my_queue()) {
      do_work();
    }
    pthread_barrier_wait(&barrier);
  }
}
这将使主线程完成工作,然后等待两个工作线程在之前完成它们所完成的工作(如果有的话)