liu2002

导航

多线程编程实践

1. 代码设计逻辑

1.1 功能要求

学习多线程编程实现以下功能:

1. 主进程从命令行接受三个参数 pa,pb,pc(pa>pb>pc)。其中 pa 表示线程 A 产生的有序数组的长度,pb 表示线程 B 的除数,pc 表示线程 C 的除数。

2. 生成并运行四个线程,各线程的工作如下: 线程 A 每隔 15 毫秒按序产生一个数(从 1 到 pa),把它们放到长度不超过 20 的 buffer 数组中。如果 buffer 已满,则需要等待空闲时再插入; 线程B每隔20毫秒查询一次buffer,统计所有能被 整除的数字的个数并求和; 线程C每隔15毫秒查询一次buffer,统计所有能被整除的数字的个数并求和; 线程 D 每隔 10 毫秒查询一次 buffer,统计所有其他数字的个数并求和;

1.2 逻辑设计

详细代码及注释请见 main.c,此处只展示大体思路。

1. 代码定义了一个缓冲区和计数器变量,以及三个求和变量和计数变量。缓冲区大小定义为 20,计数器变量 count 用于跟踪缓冲区中的元素数量。求和变 量分别为 sumB、sumC 和 sumD,用于保存过滤后的数字的总和。计数变量分别为 countB、countC 和 countD,用于跟踪过滤后的数字的数量。

2. 在本段代码中,互斥锁和条件变量的作用是同步线程之间对缓冲区的访问 和修改。当线程 A 向缓冲区添加数字时,它需要先获取互斥锁,以防止其他线 程同时修改缓冲区。当缓冲区已满时,线程 A 会等待条件变量,直到其他线程 从缓冲区中移除数字,腾出空间。一旦有空间可用,线程 A 会将数字添加到缓 冲区中,并广播条件变量,以通知其他线程缓冲区中有新的数字可用。 线程 BC 和线程 D 对缓冲区的访问和修改也需要使用互斥锁和条件变量来保证 线程之间的同步。当缓冲区为空时,线程 BC 和线程 D 会等待条件变量,直到 其他线程向缓冲区中添加数字。一旦有数字可用,它们会遍历缓冲区中的所有 数字,并对它们进行过滤和累加操作。当线程遇到终止信号(-1)时,它会广播 条件变量并解锁互斥锁,以通知其他线程退出线程函数。

3. 代码定义了三个线程函数:threadA、threadBC 和 threadD。这些线程函数将 在后面的代码中被创建和使用。

4. 代码的主函数 main 首先检查命令行参数,如果不是三个整数,则打印错误 消息并退出程序。然后,将命令行参数转换为整数,并将除数存储在数组中。 接着,代码创建了三个线程,并等待它们完成。最后,代码打印了过滤后的数 字的总和和数量。

5. 线程函数 threadA 负责生成一系列数字,并将它们添加到缓冲区中。对于每 个数字,线程函数首先休眠 15 毫秒,然后加锁,检查缓冲区是否已满。如果 缓冲区已满,则线程等待条件变量,直到缓冲区有空间可用。一旦有空间可用, 线程将数字添加到缓冲区中,增加计数器变量 count 的值。最后,线程广播条 件变量,并解锁互斥锁。如果线程已经生成了所有数字,则添加一个终止信号 (-1),并将其添加到缓冲区中。线程函数返回 NULL。

6. 线程函数 threadBC 和 threadD 负责过滤和累加缓冲区中的数字。这两个线 程的实现方式类似,只是过滤条件稍有不同。线程函数首先休眠一段时间(20 毫秒或 10 毫秒),然后加锁,检查缓冲区是否为空。如果缓冲区为空,则线程 等待条件变量,直到缓冲区中有数字可用。一旦有数字可用,线程遍历缓冲区 中的所有数字,并对它们进行过滤和累加操作。如果数字可以被给定的除数整 除,则将其累加到相应的求和变量中,并增加相应的计数变量值。最后,线程 从缓冲区中删除已处理的数字,并减少计数器变量 count 的值。如果遇到终止 信号(-1),则线程广播条件变量,并解锁互斥锁,然后返回 NULL。

7. 最后,主函数销毁互斥锁和条件变量,并打印过滤后的数字的总和和数量。

 

2. 测试示例

提供以下五组测试用例的执行结果:

./main 10 5 3

./main 100 5 3

./main 200 5 3

./main 200 10 5

./main 200 10 3

 

3. 代码展示

// 包含所需的库
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>

// 设置缓冲区大小
#define BUFFER_SIZE 20

// 声明缓冲区、计数器和求和变量
int buffer[BUFFER_SIZE];
int count = 0;
int sumB = 0, sumC = 0, sumD = 0;
int countB = 0, countC = 0, countD = 0;

// 声明互斥锁和条件变量
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

// 声明线程函数
void *threadA(void *arg);
void *threadBC(void *arg);
void *threadD(void *arg);

int main(int argc, char *argv[]) {
    // 检查命令行参数
    if (argc != 4) {
        fprintf(stderr, "usage: main <pa> <pb> <pc>\n");
        return 1;
    }

    // 转换命令行参数为整数
    int pa = atoi(argv[1]);
    int pb = atoi(argv[2]);
    int pc = atoi(argv[3]);

    // 将除数存储在数组中
    int divisors[] = {pb, pc};

    // 声明线程标识符
    pthread_t ta, tbc, td;

    // 创建线程
    pthread_create(&ta, NULL, threadA, &pa);
    pthread_create(&tbc, NULL, threadBC, divisors);
    pthread_create(&td, NULL, threadD, divisors);

    // 等待线程完成
    pthread_join(ta, NULL);
    pthread_join(tbc, NULL);
    pthread_join(td, NULL);

    // 销毁互斥锁和条件变量
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);

    // 打印结果
    printf("Thread B, %d, %d\n", countB, sumB);
    printf("Thread C, %d, %d\n", countC, sumC);
    printf("Thread D, %d, %d\n", countD, sumD);

    return 0;
}

// 线程A负责生成一系列数值
void *threadA(void *arg) {
    int pa = *((int *)arg);

    for (int i = 1; i <= pa; i++) {
        // 休眠15毫秒
        usleep(15 * 1000);

        // 加锁,并在缓冲区满时等待
        pthread_mutex_lock(&mutex);
        while (count >= BUFFER_SIZE) {
            pthread_cond_wait(&cond, &mutex);
        }

        // 向缓冲区添加数字
        buffer[count] = i;
        count++;

        // 广播条件变量并解锁
        pthread_cond_broadcast(&cond);
        pthread_mutex_unlock(&mutex);
    }

    // 添加终止信号
    pthread_mutex_lock(&mutex);
    while (count >= BUFFER_SIZE) {
        pthread_cond_wait(&cond, &mutex);
    }
    buffer[count] = -1;
    count++;
    pthread_cond_broadcast(&cond);
    pthread_mutex_unlock(&mutex);

    return NULL;
}

// 线程B和C负责过滤并累加可以被给定的两个除数整除的数值
void *threadBC(void *arg) {
    int *divisors = (int *)arg;
    int pb = divisors[0];
    int pc = divisors[1];

    while (1) {
        // 休眠20毫秒
        usleep(20 * 1000);

        // 加锁,并在缓冲区为空时等待
        pthread_mutex_lock(&mutex);
        while (count == 0) {
            pthread_cond_wait(&cond, &mutex);
        }

        // 遍历缓冲区,过滤并累加可以被给定的两个除数整除的数值
        int i = 0;
        while (i < count) {
            if (buffer[i] == -1) {
                // 如果遇到终止信号,广播条件变量并解锁
                pthread_cond_broadcast(&cond);
                pthread_mutex_unlock(&mutex);
                return NULL;
            }
            if (buffer[i] % pb == 0) {
                // 如果可以被pb整除,累加到sumB
                sumB += buffer[i];
                countB++;
                if (buffer[i] % pc == 0 && buffer[i] % pb == 0) {
                    // 如果同时可以被pc整除,累加到sumC
                    sumC += buffer[i];
                    countC++;
                }
                // 从缓冲区中移除已处理的数值
                buffer[i] = buffer[count - 1];
                count--;
            } else if (buffer[i] % pc == 0) {
                // 如果可以被pc整除,累加到sumC
                sumC += buffer[i];
                countC++;
                // 从缓冲区中移除已处理的数值
                buffer[i] = buffer[count - 1];
                count--;
            } else {
                i++;
            }
        }

        // 广播条件变量并解锁
        pthread_cond_broadcast(&cond);
        pthread_mutex_unlock(&mutex);
    }
}

// 线程D负责累加剩余的数值
void *threadD(void *arg) {
    int *divisors = (int *)arg;
    int pb = divisors[0];
    int pc = divisors[1];

    while (1) {
        // 休眠10毫秒
        usleep(10 * 1000);

        // 加锁,并在缓冲区为空时等待
        pthread_mutex_lock(&mutex);
        while (count == 0) {
            pthread_cond_wait(&cond, &mutex);
        }

        // 遍历缓冲区,累加剩余的数值
        int i = 0;
        while (i < count) {
            if (buffer[i] == -1) {
                // 如果遇到终止信号,广播条件变量并解锁
                pthread_cond_broadcast(&cond);
                pthread_mutex_unlock(&mutex);
                return NULL;
            }
            if (buffer[i] % pb != 0 && buffer[i] % pc != 0) {
                // 如果不能被pb或pc整除,累加到sumD
                sumD += buffer[i];
                countD++;
                // 从缓冲区中移除已处理的数值
                buffer[i] = buffer[count - 1];
                count--;
            } else {
                i++;
            }
        }

        // 发送条件变量信号并解锁
        pthread_cond_signal(&cond);
        pthread_mutex_unlock(&mutex);
    }
}

 

posted on 2023-07-22 21:40  柳2002  阅读(19)  评论(0编辑  收藏  举报