Переменные условия должны быть сопряжены с условием в течение некоторого общего состояния (часто называемой «предикат») - вот почему они называются переменные условия. Так, например, для запуска рабочих можно использовать простую глобальную переменную флаг:
int start_work = 0; /* Protected by WORKlock */
Тогда в рабочие потоки вы могли бы сделать:
pthread_mutex_lock(&WORKlock);
while (!start_work)
pthread_cond_wait(&WORKsig, &WORKlock);
pthread_mutex_unlock(&WORKlock);
/* this is where the work gets done */
И в главном потоке вы могли бы сделать:
pthread_mutex_lock(&WORKlock);
start_work = 1;
pthread_mutex_unlock(&WORKlock);
pthread_cond_broadcast(&WORKsig);
Вы можете видеть, что этот путь, если рабочий поток не блокируется при переменном состоянии, когда основной поток делает сигнал, start_work
будет 1, поэтому он не будет блокировать вообще.
Чтобы заблокировать основную нить до тех пор, пока рабочие не будут закончены, вы можете использовать curProc > 0
в качестве предиката. Обратите внимание, что вам не нужны как wj_varlock
, так и MAINlock
- вам просто нужно защитить переменную curProc
.
(Для того, чтобы сделать ваш дизайн правильно, что вам нужно сделать некоторые тщательное чередование условий на curProc
и start_work
)
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
int start_work = 0; /* Protected by WORKlock */
unsigned curProc; /* Protected by MAINlock */
pthread_mutex_t WORKlock = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t MAINlock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t WORKsig = PTHREAD_COND_INITIALIZER;
pthread_cond_t MAINsig = PTHREAD_COND_INITIALIZER;
void *do_work(void *t)
{
static int off; /* Protected by WORKlock */
int myoff;
pthread_mutex_lock(&WORKlock);
myoff = ++off;
pthread_mutex_unlock(&WORKlock);
while (1)
{
/* Wait to start work */
pthread_mutex_lock(&WORKlock);
while (start_work == 0)
pthread_cond_wait(&WORKsig, &WORKlock);
pthread_mutex_unlock(&WORKlock);
/* Increase number of active processes */
pthread_mutex_lock(&MAINlock);
++curProc;
pthread_mutex_unlock(&MAINlock);
pthread_cond_signal(&MAINsig);
/* this is where the work gets done */
printf("Working (%d)...\n", myoff);
sleep(1);
/* Wait for all work to be done */
pthread_mutex_lock(&WORKlock);
while (start_work == 1)
pthread_cond_wait(&WORKsig, &WORKlock);
pthread_mutex_unlock(&WORKlock);
/* Reduce number of active processes */
pthread_mutex_lock(&MAINlock);
--curProc;
if (curProc == 0)
pthread_cond_signal(&MAINsig);
pthread_mutex_unlock(&MAINlock);
}
}
int get_nprocs(void)
{
return 8;
}
int main(int argc, char **argv)
{
unsigned i, k;
pthread_t pth;
unsigned nProcs = get_nprocs(); // get number of core from system
for (i = 0; i < nProcs; ++i)
{
k = pthread_create(&pth, NULL, do_work, NULL);
if (k != 0)
{
perror("pthread_create");
exit(k);
}
}
curProc = 0;
while (1)
{
//prepare work to be done
puts("work prep");
//sleep(1);
/* Tell threads to start work */
pthread_mutex_lock(&WORKlock);
start_work = 1;
pthread_mutex_unlock(&WORKlock);
pthread_cond_broadcast(&WORKsig);
/* Wait for threads to start */
pthread_mutex_lock(&MAINlock);
while (curProc < nProcs)
pthread_cond_wait(&MAINsig, &MAINlock);
pthread_mutex_unlock(&MAINlock);
/* Tell threads not to start next lot of work */
pthread_mutex_lock(&WORKlock);
start_work = 0;
pthread_mutex_unlock(&WORKlock);
pthread_cond_broadcast(&WORKsig);
/* Wait for threads to finish */
pthread_mutex_lock(&MAINlock);
while (curProc > 0)
pthread_cond_wait(&MAINsig, &MAINlock);
pthread_mutex_unlock(&MAINlock);
}
return 0;
}
Следует отметить, что этот вид закрываемой работы более просто сделан с барьерами :
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
pthread_mutex_t WORKlock = PTHREAD_MUTEX_INITIALIZER;
pthread_barrier_t WORKbarrier;
void *do_work(void *t)
{
static int off; /* Protected by WORKlock */
int myoff;
pthread_mutex_lock(&WORKlock);
myoff = ++off;
pthread_mutex_unlock(&WORKlock);
while (1)
{
/* Wait to start work */
pthread_barrier_wait(&WORKbarrier);
/* this is where the work gets done */
printf("Working (%d)...\n", myoff);
sleep(1);
/* Wait for all work to be done */
pthread_barrier_wait(&WORKbarrier);
}
}
int get_nprocs(void)
{
return 8;
}
int main(int argc, char **argv)
{
unsigned i, k;
pthread_t pth;
unsigned nProcs = get_nprocs(); // get number of core from system
for (i = 0; i < nProcs; ++i)
{
k = pthread_create(&pth, NULL, do_work, NULL);
if (k != 0)
{
perror("pthread_create");
exit(k);
}
}
pthread_barrier_init(&WORKbarrier, NULL, nProcs + 1);
while (1)
{
//prepare work to be done
puts("work prep");
//sleep(1);
/* Tell threads to start work */
pthread_barrier_wait(&WORKbarrier);
/* Wait for threads to finish */
pthread_barrier_wait(&WORKbarrier);
}
return 0;
}
Я получаю его суть. Благодаря! –
Говорил слишком рано. Скомпилировал ваш код и запустил его. Он тоже застревает. Но ваш код работает очень быстро, у меня будут сотни итераций. Поэтому я думаю, что исправлю свой код и применим исправления к моим, если это возможно. –
@BingBang: Да, вы правы - теперь я добавил отладочную и проверенную версию. Я также добавил версию с использованием барьеров pthread, что проще. – caf