我遇到的问题是:我让我的“父母”经历一个for循环,从0到4.在每次迭代中,我有3个线程,我想要打印出12个数字的“段” (0到11)。
例如,当父循环为0时,线程0将打印出0,线程1将打印出4,线程2将打印出8。
当父循环为1时,线程0将打印出1,线程1将打印出5,线程2将打印出9。
理想情况下,输出为0 1 2 3 4 5 6 7 8 9 10 11。我知道你无法确定线程何时运行,所以它不会完全按顺序排列,但我至少希望在线程打印3 4或5之前打印0 1和2。
这就是我遇到的问题。似乎一个或另一个线程留在灰尘中并且不打印出它的段,或者没有完全打印出它的段以及其他线程。这是我正在尝试并完全理解信号量的问题。
我有两个信号量,一个在线程工作时阻塞父(生产者?),另一个阻塞每个线程,直到生产者递增到下一个索引。我认为通过这种方式我可以迫使他们在继续之前等待对方完成,但由于某种原因,我遇到了问题。
这是我的代码:
#include <semaphore.h>
#include <stdio.h>
#include <pthread.h>
#define maxNum 12
sem_t parentSem;
sem_t threadSem;
int finishedThreads;
int done = 0;
int stuff[12];
int i;
void* threadFunction(int m){
int printNum;
int baseNum;
//Determine the value the thread should start at
baseNum = (double)(maxNum/3) * m;
while(!done){ //ensure thread doesn't exit before parent is done with whole loop
//wait for parent to increment
sem_wait(&threadSem);
printNum = baseNum + i;
//keep track of how many threads are finished to let parent continue
finishedThreads++;
if(finishedThreads == 3){
//let parent continue if all threads are finished
sem_post(&parentSem);
}
}
}
int main(int argc, char** argv[]){
sem_init(&parentSem, 0, 1);
sem_init(&threadSem, 0, 0);
int rc;
pthread_t threads[3];
int l;
for(l = 0; l < 12; l++){
stuff[l] = l;
}
int j;
for(j = 0; j < 3; j++){
rc = pthread_create(&threads[i], NULL, threadFunction, (void*) j);
}
int k;
for(i = 0; i < 4; i++){
sem_wait(&parentSem); //wait for children here (initially sem set to 1)
finishedThreads = 0; //set finished thread counter to 0
for(k = 0; k < 3; k++){
//increment thread semaphore to 3 so each thread can run
sem_post(&threadSem);
}
}
for(i = 1; i < 3; i++){
pthread_join(threads[i], NULL);
}
}
如何确保所有线程在父级递增之前运行?如何确保所有线程都按“循环”运行而不会被卡在后面?有时同一个线程运行两次而不是每个线程运行一次..... help?
非常感谢你的帮助。
编辑:新代码状态:(线程函数)
while(!done){
printf("Thread %d not done yet...\n", m);
if(m == 0){
sem_wait(&threadSem0);
}else if(m == 1){
sem_wait(&threadSem1);
}else if(m == 2){
sem_wait(&threadSem2);
}
printNum = baseNum + i;
printf("Thread %d past waiting, number segment: %d\n", m, printNum);
finishedThreads++;
if(finishedThreads == 3){
sem_post(&parentSem);
}
}
父母部分:
for(i = 0; i < 4; i++){
printf("In parent for loop, counter: %d\n", i);
printf("Parent past wait semaphore\n");
finishedThreads = 0;
if(i == 3) done = 1;
sem_post(&threadSem0);
sem_post(&threadSem1);
sem_post(&threadSem2);
sem_wait(&parentSem);
}
for(i = 1; i < 3; i++){
pthread_join(threads[i], NULL);
}
答案 0 :(得分:0)
您需要访问finishedThreads
atomic以避免每个线程仅使用缓存副本。在没有优化(*)的情况下编译时你的程序是否有效?
在C ++中执行此操作的正确方法是使用std::atomic
。请参阅this question的完整答案。
如果线程快速循环,则在所有子线程中使用一个单一信号量可能会产生问题,如您的情况。您必须为每个子线程使用一个信号量来克服此问题。
sem_t threadSem[3];
// ...
sem_wait(&threadSem+m);
// ...
for (i = 0; i < 3; i++)
sem_init(threadSem+i, 0, 0);
// ...
for(k = 0; k < 3; k++){
//increment each thread semaphore so each thread can run
sem_post(threadSem+k);
}
我早期没有抓到的另外几件事:
您使用变量i
在threadFunction
中执行增量操作。这不行。正确的公式应仅取决于m
。由于每个帖子都有自己的baseNum
和printNum
副本,因此您可以毫无问题地使用它们:
baseNum = (double)(maxNum/3) * m;
while(!done){ //ensure thread doesn't exit before parent is done with whole loop
//wait for parent to increment
sem_wait(&threadSem);
printNum++;
// ....
你的代码中有很多东西取决于子线程的数量,我建议使用一个常量(就像你对maxNum
所做的那样)。
您没有设置变量done
来表示子线程的工作结束。
volatile int done;
// in threadFunction
// wait for start
sem_wait(threadSem+m)
while(!done){
// thread work here
// ...
//wait for parent to increment at the end of the loop
sem_wait(threadSem+m);
}
// in main
for(k = 0; k < 3; k++){
//increment each thread semaphore so each thread can run
if (i == 4) done = 1;
sem_post(threadSem+k);
}
当然done
也必须遵循“volatile
或互斥”规则。
您最后没有加入所有主题:
for(i = 0; i < 3; i++){
pthread_join(threads[i], NULL);
}
(*)如果您使用gcc,我的意思是使用参数-O0
,甚至使用-g
进行调试。
答案 1 :(得分:0)
你需要一个障碍,特别是一个可重复使用的障碍。看看艾伦唐尼的信号量小书,第3.6.7节,转载here(PDF)可能会有用。代码是用Python编写的,但它的要点很清楚。它需要一个互斥量和两个计数信号量,在你的情况下,你有4个参与者在障碍中。
您可以通过设置线程优先级来控制首先唤醒哪个线程,以便第一个线程是最重要的,依此类推。