使用线程的几种方式(2)工作组

本文介绍了一个基于POSIX标准的多线程程序示例,该程序使用工作组模式来处理文件搜索任务。通过创建多个线程并利用条件变量和互斥锁协调线程间的操作,实现对指定路径下的文件进行搜索。

在工作组模式中,数据由一组线程分别独立的处理。

以下例子来自于《POSIX多线程编程》,显示了一个简单的工作组。使用两个参数运行程序,一个字符串和一个搜索路径,结果类似于命令“find argv[2] -name * | xargs grep argv[1]".  程序使用一个crew来管理工作组,工作组将搜素路径排队为单链表,如果该路劲是文件,则打开搜素是否包含。

/*
 * crew.c
 * make:gcc -g -o crew -Wall -lpthread crew.c
 * 没有在程序异常情况下,对申请的资源work_t 链表释放。
 */
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include "errors.h"

#define CREW_SIZE 4
/* work_t组成单链表,其中,表头为crew->first
 * 表尾为crew->last,crew->last->nest = crew->last
 */
typedef struct work_tag {
    struct work_tag *next;
    char            *path;
    char            *string;
} work_t, *work_p;

typedef struct worter_tag {
    int             index;
    pthread_t       pthread;    
    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;

    entry = (struct dirent*)malloc(sizeof(struct dirent) + name_max);
    if(entry ==  NULL) errno_abort("malloc error");
    status = pthread_mutex_lock(&crew->mutex);
    if(status != 0) err_abort(status, "pthread_mutex_lock failed");
    while(crew->work_count == 0) {
        status = pthread_cond_wait(&crew->go, &crew->mutex);
        if(status != 0) err_abort(status, "pthread_cond_wait failed");
    }
    status = pthread_mutex_unlock(&crew->mutex);
    if(status != 0) err_abort(status, "pthread_mutex_unlock failed");
    //printf("Crew %d starting\n", mine->index); 

    while(1)
    {
        status = pthread_mutex_lock(&crew->mutex);
        if (status != 0) err_abort(status, "pthread_mutex_lock failed");
        //printf("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, "pthread_cond_wait  failed");     
        }
        
        work = crew->first;
        crew->first = crew->first->next;
        if(crew->first == NULL) {
            crew->last= NULL;
        }
        status = pthread_mutex_unlock(&crew->mutex);
        if(status != 0) err_abort(status, "pthread_mutex_unlock failed");

        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;
            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) {
                /* readdir_r 可重入函数,线程安全 */
                status = readdir_r(directory, entry, &result);
                if(status != 0){
                    fprintf(stderr, "unable to open directory %s: %d (%s)\n", work->path, status, strerror(status));            
                    break;
                }
                if(result == NULL) break;
                if(strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue;
                new_work = (work_p)malloc(sizeof(work_t));
                if(new_work == NULL) errno_abort("unable to allocate request");
                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, "pthread_mutex_lock failed");
                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++;
                status = pthread_cond_signal(&crew->go);
                status = pthread_mutex_unlock(&crew->mutex);
                if(status != 0) err_abort(status, "pthread_mutex_unlock failed");
            }
            closedir(directory);
       } else if(S_ISREG(filestat.st_mode)) {
           FILE *search;
           char buffer[256], *buf_ptr, *search_ptr;
           if((search = fopen(work->path, "r")) == NULL)
               fprintf(stderr, "unable to open file %s: %d (%s)\n", work->path, status, strerror(status));            
           else {
               while((buf_ptr = fgets(buffer, sizeof(buffer), search)) != NULL) {
                   search_ptr = strstr(buffer, work->string);
                   if(search_ptr != NULL){
                       printf("thread %d found \"%s\" in %s \n", mine->index, work->string, work->path);
                       break;
                   }
               }
           } 
           fclose(search); 
       } else {
           fprintf(stderr, "thred %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(work);

       status = pthread_mutex_lock(&crew->mutex);
       if(status != 0) err_abort(status, "pthread_mutex_lock failed");
       crew->work_count--;
       if(crew->work_count <= 0) {
           status = pthread_cond_broadcast(&crew->done);
           if(status != 0) err_abort(status, "pthread_cond_broadcast failed"); 
           status = pthread_mutex_unlock(&crew->mutex);
           if(status != 0) err_abort(status, "pthread_mutex_unlock failed");
           break;     
       }
       status = pthread_mutex_unlock(&crew->mutex);
       if(status != 0) err_abort(status, "pthread_mutex_unlock failed");
    }

    free(entry);
    return (void*)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;
    
    /*等待crew完成*/
    while(crew->work_count >0){
        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 = 256;
        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");
    }  
    
    //printf("PATH_MAX for %s is %ld, NAME_MAX is %ld", filepath, path_max, name_max); 
    path_max++;
    name_max++;

    request = (work_p)malloc(sizeof(work_t));
    if(request == NULL) errno_abort("unable to allocate request");
    
    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, "pthread_cond_wait failed");
    }
    status = pthread_mutex_unlock(&crew->mutex);
    if(status != 0) err_abort(status, "pthread_mutex_unlock failed");
    return 0;
}

int crew_create(crew_t *crew, int crew_size)
{
    int status;
    int crew_index;

    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->go, NULL);
    if (status != 0) return status; 
    status = pthread_cond_init(&crew->done, 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].pthread, NULL, 
                                worker_routine, (void*)&crew->crew[crew_index]);
        if(status != 0) err_abort(status, "pthread_create failed"); 
    }
    return 0;
}

int main(int argc, char *argv[])
{
    crew_t my_crew;
    int status;

    if(argc < 3) {
        fprintf(stderr, "Usage:%s string path\n", argv[0]);
        exit(-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;
}


<think>首先,用户的问题是:“我想了解线程之间的通信方式种类请问线程之间有哪些通信方式”。这需要我回答线程间通信的各种方式。根据系统级指令:-所有行内数学表达式必须使用$...$格式。-独立公式使用$$...$$格式。-使用中文回答。-生成相关问题。-在回答中引用的段落末尾自然地添加引用标识,如[^1]。-回答结构清晰,帮助用户逐步解决问题,保证回答真实可靠。用户提供了几个引用:-引用[1]:提到线程间通信方式包括全局变量方式、消息传递方式、参数传递方式线程同步法。-引用[2]:提到共享内存。-引用[3]:提到消息传递通过队列(Queue)。-引用[4]:提到BlockingQueue用于生产者-消费者模型。-引用[5]:提到使用synchronized进行通信。我需要整合这些信息,构建一个清晰的回答。线程间通信方式的主要种类:1.**共享内存(SharedMemory)**:线程通过共享变量或数据结构进行通信。这包括全局变量。需要同步机制来避免竞争条件。2.**消息传递(MessagePassing)**:线程通过发送和接收消息来通信。常见实现包括队列(如BlockingQueue)。3.**参数传递(ParameterPassing)**:线程创建时通过参数传递数据。4.**同步机制(SynchronizationMechanisms)**:使用锁、信号量等同步工具进行通信和协调。从引用中提取:-引用[1]:全局变量方式、消息传递方式、参数传递方式线程同步法。-引用[2]:共享内存。-引用[3]:消息传递通过队列。-引用[4]:BlockingQueue用于生产者-消费者。-引用[5]:synchronized用于通信。所以,我可以组织回答如下:1.**共享内存/全局变量**:线程访问共享数据。2.**消息传递**:使用队列或管道。3.**参数传递**:线程创建时传递初始数据。4.**同步机制**:如锁、信号量,用于协调访问。在回答中,需要自然地添加引用标识。例如,当提到全局变量时,引用[^1];当提到队列时,引用[^3]或[^4]。回答结构:-先简要介绍线程间通信。-然后列出主要方式,逐一解释。-确保语言中文。-最后生成相关问题。生成相关问题:-根据用户问题,相关问题是关于线程通信的进一步内容。-例如:如何选择线程间通信方式线程通信中如何避免死锁?共享内存vs消息传递的优缺点?现在,构建回答。**介绍部分**:线程间通信是为了协调线程工作,避免竞争和数据不一致。**主要通信方式**:1.**共享内存(SharedMemory)**:线程通过共享变量通信。需同步,如synchronized[^5]。引用[^1]提到全局变量方式2.**消息传递(MessagePassing)**:线程发送和接收消息,如使用队列[^3][^4]。常用于生产者-消费者模型。3.**参数传递(ParameterPassing)**:线程创建时通过参数传递数据[^1]。4.**同步机制(Synchronization)**:使用锁、信号量等协调线程[^5]。确保使用中文。添加引用:在解释每个点时,自然地添加引用。例如:-"在共享内存方式中,线程访问全局变量进行通信[^1]。"-"消息传递可以通过队列实现,如Java中的BlockingQueue[^3][^4]。"-"参数传递方式在创建线程时指定数据[^1]。"-"线程同步法使用synchronized等机制确保安全通信[^5]。"最后,生成相关问题。完整回答框架:1.开头:解释线程间通信的重要性。2.主体:列出并描述主要通信方式。3.结尾:总结或过渡。4.相关问题。数学表达式:用户问题没有涉及数学,但如果有变量,用$...$。这里可能不需要,除非举例。例如,如果提到计数器,可以用$counter$。但用户问题主要是概念性的,所以可能不需要数学。现在,写回答。</think>### 线程间通信方式种类 线程间通信是指多个线程之间交换信息、协调工作以完成特定任务的过程。这在多线程编程中至关重要,因为它能避免竞争条件、数据不一致和死锁等问题[^1]。线程通信的主要目的是确保线程有序执行,提高程序的可靠性和效率。下面我将详细介绍线程间通信的主要方式,基于常见编程实践和引用资料。 #### 1. **共享内存(Shared Memory)** - **描述**:线程通过访问共享的变量或数据结构(如全局变量)来传递信息。这种方式高效但需要同步机制来防止并发问题。例如,一个线程写入数据到共享变量,另一个线程读取该变量[^1][^2]。 - **关键点**: - 常用同步工具:如 `synchronized` 关键字(在Java中)或锁(Lock),确保同一时间只有一个线程访问共享资源,避免数据冲突[^5]。 - 优点:速度快,适合高频数据交换。 - 缺点:易引发竞争条件或死锁,需谨慎设计。 - **示例**:在C++或Java中,定义一个全局变量 $counter$,多个线程通过加锁(如 `synchronized`)来更新和读取它[^1][^5]。 #### 2. **消息传递(Message Passing)** - **描述**:线程通过发送和接收消息来通信,而非直接共享内存。消息通常通过队列(Queue)传递,一个线程生产消息放入队列,另一个线程消费消息[^3][^4]。 - **关键点**: - 常用实现:如Java的 `BlockingQueue`,它支持线程安全的入队和出队操作,常用于生产者-消费者模型[^4]。 - 优点:解耦线程,减少同步需求,避免死锁;适合分布式系统。 - 缺点:可能有性能开销,不适合实时性要求高的场景。 - 数学表达式示例:如果队列大小为 $N$,生产者线程每秒生成 $k$ 条消息,消费者处理速率需匹配以避免溢出[^3][^4]。 #### 3. **参数传递(Parameter Passing)** - **描述**:线程在创建时通过参数接收初始数据。这是一种单向通信方式,适用于线程启动时的初始化[^1]。 - **关键点**: - 优点:简单直接,无共享状态问题。 - 缺点:只适用于初始通信,不适用于运行时动态交互;线程间后续通信需其他方式补充。 - **示例**:在Python中,创建线程时传递参数: ```python import threading def worker(data): print(f"Received data: {data}") thread = threading.Thread(target=worker, args=("initial data",)) thread.start() ``` #### 4. **同步机制(Synchronization)** - **描述**:使用同步原语协调线程的执行顺序,实现间接通信。这包括锁、信号量(Semaphore)、条件变量(Condition Variable)等[^1][^5]。 - **关键点**: - 常用场景:确保线程在特定条件下才执行,如等待资源可用或事件触发。 - 优点:解决依赖关系,保证计算正确性;可避免死锁通过超时机制。 - 缺点:可能引入阻塞,影响性能。 - **示例**:在Java中,使用 `wait()` 和 `notify()` 方法实现线程等待和唤醒,依赖于对象锁[^5]。 #### 总结 线程间通信的核心方式包括共享内存、消息传递、参数传递和同步机制。选择哪种方式取决于应用场景: - **共享内存** 适合高性能、低延迟需求,但需严格同步[^2][^5]。 - **消息传递** 适合解耦和可扩展系统,如微服务架构[^3][^4]。 - **参数传递** 适用于简单初始化。 - **同步机制** 是基础,常用于补充其他方式以解决竞争和死锁问题[^1]。 在实际编程中,常组合使用多种方式。例如,生产者-消费者模型结合消息传递(队列)和同步(锁)来实现高效通信[^4]。线程通信设计时,需注意避免死锁(如通过死锁检测算法)和优化性能[^1]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值