Day03 多线程编程模型--工作组

可以是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;
}

转载于:https://www.cnblogs.com/jfm-cs/p/7956339.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值