多线程编程模型之工作组模式

来源:互联网 发布:js监听ios软键盘事件 编辑:程序博客网 时间:2024/06/05 22:27
本例子代码改编自<POSIX多线程程序设计>
原题:主程序输入一个路径和字符串。程序递归搜索路径下所有的文件,并查找文件里是否有匹配的字符串,如果有则打印。

改编后的程序封装性更好,数据结构跟操作分离得更好,在设计上抽象度更好。


#include <pthread.h>#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <dirent.h>#include <errno.h>#include <string.h>#include <unistd.h>#include <sys/stat.h>#include "osa_debug.h"#include "osa_debug.c"#define CREW_SIZE 4size_t path_max;size_t name_max;typedef struct work_tag{char *path;char *string;struct work_tag *next;}work_t;typedef struct work_queue_tag{int work_count;work_t *first; work_t *last;} work_queue_t;work_t *work_alloc(char *path, char *search){work_t *w = (work_t *)malloc( sizeof(work_t) );if( w == NULL ){//return NULL;}w->path = (char *)malloc( sizeof(char)*path_max );if( w->path == NULL ){//return NULL;}strcpy( w->path, path );w->string = search;w->next = NULL;return w;}void work_enqueue(work_queue_t *q, work_t *w){if( q->work_count == 0 ){q->first = q->last = w;q->work_count++;return;}q->last->next = w;q->last = w;q->work_count++;}work_t *work_dequeue(work_queue_t *q){work_t *w = q->first;q->first = q->first->next;w->next = NULL;q->work_count--;return w;}int work_queue_init(work_queue_t **q){work_queue_t *tmp = (work_queue_t *)malloc(sizeof(work_queue_t));if( tmp == NULL ){//return -1;}tmp->work_count = 0;tmp->first = NULL;tmp->last = NULL;*q = tmp;return 0;}struct crew_tag;typedef struct worker_tag{int index;pthread_t pid;struct crew_tag *crew;}worker_t;typedef struct crew_tag{int crew_size;worker_t crew[CREW_SIZE];work_queue_t *workQueue;pthread_mutex_t mutex;pthread_cond_t condDone;pthread_cond_t condGo;}crew_t;int GetWorkCount(crew_t *c){return c->workQueue->work_count;}work_t *worker_dequeue(crew_t *c){//OSA_FUNC_ENTRY;pthread_mutex_lock( &(c->mutex));//if crew is empty, wait for messagewhile( GetWorkCount(c) == 0){pthread_cond_wait( &(c->condGo), &(c->mutex) );}work_t *w = NULL;w = work_dequeue( c->workQueue );pthread_mutex_unlock( &(c->mutex) );return w;}void worker_enqueue(crew_t *c, work_t *w){//OSA_FUNC_ENTRY;pthread_mutex_lock( &(c->mutex) );work_enqueue( c->workQueue, w );pthread_cond_broadcast( &(c->condGo) );pthread_mutex_unlock( &(c->mutex) );}void worker_wait_for_empty(crew_t *c){//OSA_FUNC_ENTRY;pthread_mutex_lock( &(c->mutex) );//if crew is busy, wait for it to finishif( GetWorkCount(c) > 0 ){pthread_cond_wait( &(c->condDone), &(c->mutex) );}pthread_mutex_unlock( &(c->mutex) );}void worker_signal_work_done(crew_t *c){//OSA_FUNC_ENTRY;pthread_mutex_lock( &(c->mutex) );if( GetWorkCount(c) == 0 ){pthread_cond_broadcast( &(c->condDone) );}pthread_mutex_unlock( &(c->mutex) );}void searchStr(const char *file, char *key, int workerId){FILE *search;char buf[256];char *bufptr;char *searchptr;search = fopen(file, "r");if( search == NULL ){fprintf(stderr, "unable to open regular file %s, err:%s \n", file, strerror(errno));}else{while(1){bufptr = fgets(buf, sizeof(buf), search);if( bufptr == NULL ){if( feof(search) ){break;}if( ferror(search) ){fprintf(stderr, "unable to open read file %s, err:%s \n", file, strerror(errno));break;}}searchptr = strstr( buf, key );if( searchptr != NULL ){fprintf(stderr, "worker %d found \"%s\" in %s\n", workerId, key, file );break;}}}fclose(search);}void *workder_routine(void *arg){//OSA_FUNC_ENTRY;worker_t *wk = (worker_t *)arg;crew_t *c = wk->crew;work_t *w = NULL;work_t *nw = NULL;struct stat filestat;struct dirent *entry;entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max);if( entry == NULL ){//return NULL;}while(1){w = worker_dequeue(c);lstat(w->path, &filestat);if( S_ISLNK( filestat.st_mode ) ){fprintf(stderr, "%s a link, skipping\n", w->path);}else if( S_ISDIR( filestat.st_mode ) ){DIR *dir;struct dirent *result;dir = opendir(w->path);if( dir == NULL ){fprintf(stderr, "unable to open directory %s, err:%s \n", w->path, strerror(errno));continue;}while(1){int ret = readdir_r(dir, entry, &result);if( ret != 0 ){fprintf(stderr, "unable to open directory %s, err:%s \n", w->path, strerror(ret));break;}if(result == NULL){break;}if( strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0 ){continue;}nw = (work_t *)malloc( sizeof(work_t) );if( nw == NULL ){//}nw->path = (char *)malloc( sizeof(char) * path_max );if( nw->path == NULL ){//}strcpy(nw->path, w->path);strcat(nw->path, "/");strcat(nw->path, entry->d_name);nw->string = w->string;nw->next = NULL;worker_enqueue(c, nw);}closedir(dir);}//end of  if(S_ISDIR())else if( S_ISREG(filestat.st_mode) ){searchStr(w->path, w->string, wk->index);}else{ fprintf(stderr, "thread %d: %s is type %s\n",  wk->index,  w->path,  S_ISFIFO( filestat.st_mode ) ? "FIFO" : S_ISCHR( filestat.st_mode ) ? "CHILD" :S_ISBLK( filestat.st_mode ) ? "BLOCK" :S_ISSOCK( filestat.st_mode ) ? "SOCK" :"unknown");}free(w->path);free(w);worker_signal_work_done(c);}free(entry);}int crew_create(crew_t *c, int crew_size){//OSA_FUNC_ENTRY;if( crew_size > CREW_SIZE){//return -1;}c->crew_size = crew_size;//init work queuework_queue_init( &(c->workQueue) );pthread_mutex_init( &(c->mutex), NULL );pthread_cond_init( &(c->condDone), NULL );pthread_cond_init( &(c->condGo), NULL );pthread_setconcurrency(10);int i;for( i=0; i< crew_size; i++ ){c->crew[i].index = i;c->crew[i].crew = c;pthread_create( &(c->crew[i].pid), NULL, workder_routine, (void *)&(c->crew[i]) );}return 0;}int crew_start( crew_t *c, char *filepath, char *search ){//OSA_FUNC_ENTRY;//if crew is busy, wait for it to finishworker_wait_for_empty(c);path_max = name_max = 1024;work_t *w = work_alloc(filepath, search);//insert work to work queueworker_enqueue(c, w);worker_wait_for_empty(c);}int main( int argc, char *argv[] ){crew_t mycrew;crew_create( &mycrew, CREW_SIZE );crew_start(&mycrew, argv[1], argv[2] );}

工作组模型
1.多个worker共享一个数据队列,所以每次dequeue时要先检查队列是否为空。不为空才能的返回消息。
2.enqueue可以不用关心队列消息数,只需要enqueue然后通知线程即可。
3.示例中,条件变量condDone是可选的。某些情况下需要知晓队列是否为空的状态,可以考虑增加这个条件变量并编写接口。

0 0