可以是SIMD(Single Instruction, Multiple data) 也可以是MIMD(Multiple Instruction, Multiple data).
程序示例:
首先, crew_create创建工作组,工作组大小为CREW_SIZE 。 crew_t 中包含工作计数work_count, job链表(first, last) 以及worker数组。 首先,启动worker 线程。 此时work_count 尚为0,线程等待在crew->go 条件变量上。
在主线程内,接收用户正确输入后,调用crew_start函数。
crew_start函数,开始运行时,work_count为0,根据用户的输入,生成第一个work_t 的任务,并插入到crew_t 的job链表中,增加work_count变量,然后 signal go 条件变量。
之前,等待go条件变量的worker线程可以继续工作。
crew_start 等待在done条件变量上,检测work_count. 看是否还有任务没有处理。 等到任务处理完成,函数退出,返回主线程。
worker工作线程,依次取出job链表中的任务,并执行任务,如果是文件,则打开文件,检索字符串,如果是目录,则将每一个子目录及文件加入job链表中。
检测work_count的值,每执行完任务后,将其--,如果任务全部执行完毕,则brodecast done条件变量。 crew_start接收到广播,则可返回主进程,程序结束。
crew.c
/************************************************************************* * File Name: crew.c * Author: JohnMiller * Mail: jfmcs_0211@126.com * Created Time: Sat 02 Dec 2017 08:21:38 PM CST *************************************************************************/ #include <stdio.h> #include <pthread.h> #include <sys/types.h> #include <sys/stat.h> #include <dirent.h> #include "../errors/errors.h" #define CREW_SIZE 4 typedef struct work_tag { struct work_tag *next; /* next work item*/ char *path; char *string; } work_t, *work_p; typedef struct worker_tag { int index; pthread_t thread; struct crew_tag *crew; } worker_t, *worker_p; typedef struct crew_tag { int crew_size; worker_t crew[CREW_SIZE]; long work_count; work_t *first, *last; pthread_mutex_t mutex; pthread_cond_t done; pthread_cond_t go; } crew_t, *crew_p; size_t path_max; size_t name_max; void *worker_routine (void *arg) { worker_p mine = (worker_t*)arg; crew_p crew = mine->crew; work_p work, new_work; struct stat filestat; struct dirent *entry; int status; /* * "struct dirent" is funny, because POSIX doesn't require * the definition to be more than a header for a variable * buffer. Thus, allocate a "big chunk" of memory, and use * it as a buffer. */ entry = (struct dirent*)malloc ( sizeof (struct dirent) + name_max); if (entry == NULL) errno_abort ("Allocating dirent"); status = pthread_mutex_lock (&crew->mutex); if (status != 0) err_abort (status, "Lock crew mutex"); /* * There won't be any work when the crew is created, so wait * until something's put on the queue. */ while (crew->work_count == 0) { status = pthread_cond_wait (&crew->go, &crew->mutex); if (status != 0) err_abort (status, "Wait for go"); } status = pthread_mutex_unlock (&crew->mutex); if (status != 0) err_abort (status, "Unlock mutex"); DPRINTF (("Crew %d starting\n", mine->index)); /* * Now, as long as there's work, keep doing it. */ while (1) { /* * Wait while there is nothing to do, and * the hope of something coming along later. If * crew->first is NULL, there's no work. But if * crew->work_count goes to zero, we're done. */ status = pthread_mutex_lock (&crew->mutex); if (status != 0) err_abort (status, "Lock crew mutex"); DPRINTF (("Crew %d top: first is %#lx, count is %d\n", mine->index, crew->first, crew->work_count)); while (crew->first == NULL) { status = pthread_cond_wait (&crew->go, &crew->mutex); if (status != 0) err_abort (status, "Wait for work"); } DPRINTF (("Crew %d woke: %#lx, %d\n", mine->index, crew->first, crew->work_count)); /* * Remove and process a work item */ work = crew->first; crew->first = work->next; if (crew->first == NULL) crew->last = NULL; DPRINTF (("Crew %d took %#lx, leaves first %#lx, last %#lx\n", mine->index, work, crew->first, crew->last)); status = pthread_mutex_unlock (&crew->mutex); if (status != 0) err_abort (status, "Unlock mutex"); /* * We have a work item. Process it, which may involve * queuing new work items. */ status = lstat (work->path, &filestat); if (S_ISLNK (filestat.st_mode)) printf ( "Thread %d: %s is a link, skipping.\n", mine->index, work->path); else if (S_ISDIR (filestat.st_mode)) { DIR *directory; struct dirent *result; /* * If the file is a directory, search it and place * all files onto the queue as new work items. */ directory = opendir (work->path); if (directory == NULL) { fprintf ( stderr, "Unable to open directory %s: %d (%s)\n", work->path, errno, strerror (errno)); continue; } while (1) { status = readdir_r (directory, entry, &result); if (status != 0) { fprintf ( stderr, "Unable to read directory %s: %d (%s)\n", work->path, status, strerror (status)); break; } if (result == NULL) break; /* End of directory */ /* * Ignore "." and ".." entries. */ if (strcmp (entry->d_name, ".") == 0) continue; if (strcmp (entry->d_name, "..") == 0) continue; new_work = (work_p)malloc (sizeof (work_t)); if (new_work == NULL) errno_abort ("Unable to allocate space"); new_work->path = (char*)malloc (path_max); if (new_work->path == NULL) errno_abort ("Unable to allocate path"); strcpy (new_work->path, work->path); strcat (new_work->path, "/"); strcat (new_work->path, entry->d_name); new_work->string = work->string; new_work->next = NULL; status = pthread_mutex_lock (&crew->mutex); if (status != 0) err_abort (status, "Lock mutex"); if (crew->first == NULL) { crew->first = new_work; crew->last = new_work; } else { crew->last->next = new_work; crew->last = new_work; } crew->work_count++; DPRINTF (( "Crew %d: add work %#lx, first %#lx, last %#lx, %d\n", mine->index, new_work, crew->first, crew->last, crew->work_count)); status = pthread_cond_signal (&crew->go); status = pthread_mutex_unlock (&crew->mutex); if (status != 0) err_abort (status, "Unlock mutex"); } closedir (directory); } else if (S_ISREG (filestat.st_mode)) { FILE *search; char buffer[256], *bufptr, *search_ptr; /* * If this is a file, not a directory, then search * it for the string. */ search = fopen (work->path, "r"); if (search == NULL) fprintf ( stderr, "Unable to open %s: %d (%s)\n", work->path, errno, strerror (errno)); else { while (1) { bufptr = fgets ( buffer, sizeof (buffer), search); if (bufptr == NULL) { if (feof (search)) break; if (ferror (search)) { fprintf ( stderr, "Unable to read %s: %d (%s)\n", work->path, errno, strerror (errno)); break; } } search_ptr = strstr (buffer, work->string); if (search_ptr != NULL) { flockfile (stdout); printf ( "Thread %d found \"%s\" in %s\n", mine->index, work->string, work->path); #if 0 printf ("%s\n", buffer); #endif funlockfile (stdout); break; } } fclose (search); } } else fprintf ( stderr, "Thread %d: %s is type %o (%s))\n", mine->index, work->path, filestat.st_mode & S_IFMT, (S_ISFIFO (filestat.st_mode) ? "FIFO" : (S_ISCHR (filestat.st_mode) ? "CHR" : (S_ISBLK (filestat.st_mode) ? "BLK" : (S_ISSOCK (filestat.st_mode) ? "SOCK" : "unknown"))))); free (work->path); /* Free path buffer */ free (work); /* We're done with this */ /* * Decrement count of outstanding work items, and wake * waiters (trying to collect results or start a new * calculation) if the crew is now idle. * * It's important that the count be decremented AFTER * processing the current work item. That ensures the * count won't go to 0 until we're really done. */ status = pthread_mutex_lock (&crew->mutex); if (status != 0) err_abort (status, "Lock crew mutex"); crew->work_count--; DPRINTF (("Crew %d decremented work to %d\n", mine->index, crew->work_count)); if (crew->work_count <= 0) { DPRINTF (("Crew thread %d done\n", mine->index)); status = pthread_cond_broadcast (&crew->done); if (status != 0) err_abort (status, "Wake waiters"); status = pthread_mutex_unlock (&crew->mutex); if (status != 0) err_abort (status, "Unlock mutex"); break; } status = pthread_mutex_unlock (&crew->mutex); if (status != 0) err_abort (status, "Unlock mutex"); } free (entry); return NULL; } int crew_create(crew_t *crew, int crew_size) { int crew_index; int status; if(crew_size > CREW_SIZE) return EINVAL; crew->crew_size = crew_size; crew->work_count = 0; crew->first = NULL; crew->last = NULL; status = pthread_mutex_init(&crew->mutex,NULL); if(status != 0) return status; status = pthread_cond_init(&crew->done,NULL); if(status != 0) return status; status = pthread_cond_init(&crew->go,NULL); if(status != 0) return status; for(crew_index = 0; crew_index<CREW_SIZE; crew_index ++) { crew->crew[crew_index].index = crew_index; crew->crew[crew_index].crew = crew; status = pthread_create(&crew->crew[crew_index].thread, NULL,worker_routine,(void *)&crew->crew[crew_index]); if(status != 0) err_abort(status,"Create worker"); } return 0; } int crew_start(crew_p crew, char *filepath, char *search) { work_p request; int status; status = pthread_mutex_lock(&crew->mutex); if(status != 0) return status; while(crew->work_count > 0) { status = pthread_cond_wait(&crew->done,&crew->mutex); if(status != 0) { pthread_mutex_unlock(&crew->mutex); return status; } } errno = 0; path_max = pathconf(filepath,_PC_PATH_MAX); if(path_max == -1) { if(errno == 0 ) path_max = 1024; else errno_abort("unable to get PATH_MAX"); } errno = 0; name_max = pathconf(filepath,_PC_NAME_MAX); if(name_max == -1) { if(errno == 0) name_max = 256; else errno_abort("unable to get NAME_MAX"); } DPRINTF(("PATH_MAX for %s is %ld, NAME_MAX is %ld\n", filepath, path_max, name_max)); path_max ++; name_max ++; request = (work_p) malloc(sizeof(work_t)); if(request == NULL) errno_abort("unable to allocate path"); DPRINTF(("Requesting %s\n",filepath)); request->path = (char *)malloc(path_max); if(request->path == NULL) errno_abort("unable to allocate path"); strcpy(request->path,filepath); request->string = search; request->next = NULL; if(crew->first == NULL) { crew->first = request; crew->last = request; } else { crew->last->next = request; crew->last = request; } crew->work_count ++; status = pthread_cond_signal(&crew->go); if(status != 0) { free(crew->first); crew->first = NULL; crew->work_count = 0; pthread_mutex_unlock(&crew->mutex); return status; } while(crew->work_count > 0) { status = pthread_cond_wait(&crew->done,&crew->mutex); if(status != 0) err_abort(status,"waiting for crew to finish"); } status = pthread_mutex_unlock(&crew->mutex); if(status != 0) err_abort (status,"unlock crew mutex"); return 0; } int main(int argc, char *argv[] ) { crew_t my_crew; char line[128], *next; int status; if(argc < 3) { fprintf(stderr,"Usage: %s string path\n",argv[0]); return -1; } status = crew_create(&my_crew,CREW_SIZE); if(status != 0) err_abort(status,"Create crew"); status = crew_start(&my_crew, argv[2],argv[1]); if(status != 0) err_abort(status,"start crew"); return 0; }