《POSIX多线程程序设计》 多线程之流水线模型
示例代码:
pipe_create函数创建一个链表,每一个结点,代表流水线中的一个状态,并在最后附件一个结果状态。之后,启动状态线程pipe_stage,此时stage->data_ready 尚为0,pipe_stage等待条件变量avail.
若主线程接受到的输入合法,则调用pipe_start函数,记录进入pipeline的数据数量。pipe_start中调用pipe_send 发送到pipeline中的第一个结点。pipe_send接收到数据后,置stage->data_ready为1,(表示暂不能接收下一个pip_start调用发送过来的数据),signal avail变量,使pipe_stage将数据,接收并处理。
pipe_stage 接收数据并处理后(此处简单+1),调用pipe_send发送到下一个结点。并把stage->data_ready置0(表明该流水线结点,数据已经被取走处理,可以接收新数据),并signal ready变量,使当前流水线结点接收数据,并处理。
当主线程中接收到打印结果的命令时,调用pipe_result。pipe_result检查pipeline中是否有数据,若有数据,则等待tail->data_ready信号量,如果tail中有结果,则取走该结果,并signal ready信号量,供前面的线程存放产生的结果。
/************************************************************************* * File Name: pipe.c * Author: JohnMiller * Mail: jfmcs_0211@126.com * Created Time: Sat 02 Dec 2017 11:12:41 AM CST *************************************************************************/ #include <stdio.h> #include <pthread.h> #include "../errors/errors.h" typedef struct stage_tag { pthread_mutex_t mutex; pthread_cond_t avail; pthread_cond_t ready; int data_ready; long data; pthread_t thread; struct stage_tag *next; } stage_t; typedef struct pipe_tag { pthread_mutex_t mutex; stage_t *head; stage_t *tail; int stages; int active; } pipe_t; int pipe_send(stage_t *stage, long data) { int status; status = pthread_mutex_lock(&stage->mutex); if(status != 0) return status; while(stage->data_ready) { status=pthread_cond_wait(&stage->ready,&stage->mutex); if(status != 0) { pthread_mutex_unlock(&stage->mutex); return status; } } stage->data = data; stage->data_ready = 1; status = pthread_cond_signal(&stage->avail); if(status != 0) { pthread_mutex_unlock(&stage->mutex); return status; } status = pthread_mutex_unlock(&stage->mutex); return status; } void *pipe_stage(void *arg) { stage_t *stage = (stage_t *)arg; stage_t *next_stage = stage->next; int status; status = pthread_mutex_lock(&stage->mutex); if(status != 0) err_abort(status,"Lock pipe stage"); while(1) { while(stage->data_ready != 1) { status = pthread_cond_wait(&stage->avail, &stage->mutex); if(status != 0) err_abort(status,"wait for previous stage"); } pipe_send(next_stage,stage->data+1); stage->data_ready = 0; status = pthread_cond_signal(&stage->ready); if(status != 0) err_abort(status,"wake next stage"); } /* Notice that the routine never unlocks the stage->mutex. * The call to pthread_cond_wait implicitly unlocks the * mutex while the thread is waiting, allowing other threads * to make progress. Because the loop never terminates, this * function has no need to unlock the mutex explicitly. */ } int pipe_create(pipe_t *pipe, int stages) { int pipe_index; stage_t **link = &pipe->head, *new_stage, *stage; int status; status = pthread_mutex_init(&pipe->mutex, NULL); if(status != 0) err_abort(status, "Init pipe mutex"); pipe->stages = stages; pipe->active = 0; for(pipe_index = 0; pipe_index <= stages; pipe_index ++) { new_stage = (stage_t *)malloc(sizeof(stage_t)); if(new_stage == NULL) errno_abort("Allocate stage"); status = pthread_mutex_init(&new_stage->mutex, NULL); if(status != 0) err_abort(status,"Init stage mutex"); status = pthread_cond_init(&new_stage->avail,NULL); if(status != 0) err_abort(status,"Init avail condition"); status = pthread_cond_init(&new_stage->ready, NULL); if(status != 0) err_abort(status,"Init ready condition"); new_stage->data_ready = 0; *link = new_stage; link = &new_stage->next; } *link = (stage_t *) NULL; /* Terminate list */ pipe->tail = new_stage; /* Record the tail*/ for( stage = pipe->head; stage->next != NULL; stage = stage->next) { status = pthread_create(&stage->thread,NULL,pipe_stage, (void*) stage); if(status != 0) err_abort(status, "create pipe stage"); } return 0; } int pipe_start(pipe_t *pipe, long value) { int status; status = pthread_mutex_lock(&pipe->mutex); if(status != 0) err_abort(status, "Lock pipe mutex"); pipe->active ++; status = pthread_mutex_unlock(&pipe->mutex); if(status != 0) err_abort(status, "unlock pipe mutex"); pipe_send(pipe->head,value); return 0; } int pipe_result(pipe_t *pipe, long *result) { stage_t *tail = pipe->tail; long value; int empty = 0; int status; status = pthread_mutex_lock(&pipe->mutex); if(status != 0) err_abort(status,"Lock pipe mutex"); if(pipe->active <= 0) empty = 1; else pipe->active--; status = pthread_mutex_unlock(&pipe->mutex); if(status != 0) err_abort(status, "unlock pipe mutex"); if(empty) return 0; pthread_mutex_lock(&tail->mutex); while(!tail->data_ready) pthread_cond_wait(&tail->ready,&tail->mutex); *result = tail->data; tail->data_ready = 0; pthread_cond_signal(&tail->ready); pthread_mutex_unlock(&tail->mutex); return 1; } int main(int argc, char *argv[]) { pipe_t my_pipe; long value, result; int status; char line[128]; pipe_create(&my_pipe,10); printf("Enter integer values, or \"=\" for next result\n"); while(1) { printf("Data> "); if(fgets(line,sizeof(line),stdin) == NULL) exit(0); if(strlen(line) <=1) continue; if(strlen(line) <= 2 && line[0] == '='){ if(pipe_result(&my_pipe,&result)) printf("Result is %ld\n",result); else printf("pipe is empty\n"); } else { if(sscanf(line,"%ld",&value) <1 ) fprintf(stderr,"Enter an integer value\n"); else pipe_start(&my_pipe, value); } } return 0; }