c入门第二十五篇: 学生成绩管理系统优化(多线程)

前言

程夏:“你这个系统不好用啊?连上之后没有反应,是不是挂了?”
客户端程序
师弟:“不应该啊,我这边好好的,可以正常操作的。”
客户端程序
程夏:“我这边有问题,大概率还是你系统问题。你再测试一下吧。”
师弟:“额,基本的功能我都测试过了的。”
程夏:“多客户端请求呢?一些边界场景、一些异常场景都有测试么?”
师弟瞬时陷入沉思,难道是多客户端操作存在问题。
师弟紧急复现了一下这个问题,发现服务端,最多处理一个客户端的请求,其他请求阻塞住了。
如何支持多客户端的请求呢?

多线程

在c语言中可以通过 pthread_create 函数来实现多线程的创建。主要代码处理逻辑片段如下。在主线程中创建完处理线程之后,主线程继续执行后面的逻辑,不会被阻塞,客户端的请求则被新线程处理。好比汽车维修的接待和技术维修,如果是同一个人,则同时只能进行一辆车的处理,但是如果接待是一个人和维修是多个人,接待完分发给维修处理之后,继续接待,则可以进行多辆车的处理。多线程的优点是,在cpu多核架构中,能够充分利用多个核心的并发处理来提交程序的效率。

    while (1) {
        // Accept a connection
        if ((conn_fd = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
            perror("accept");
            exit(EXIT_FAILURE);
        }

        // 启动线程去处理数据
        ret = pthread_create(&thread, NULL, do_data_process, &conn_fd);
        if (ret) {
            printf("pthread_create failed, ret:%d\n", ret);
            exit(EXIT_FAILURE);
        }
    }

多线程

系统完整代码如下

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h> // for access() function
#include <sys/socket.h>
#include <netinet/in.h>
#include <pthread.h>

#define MAX_STUDENTS 100
#define MAX_NAME_LEN 50
#define STUDENT_SYSTEM "student_system"
#define TRUE 1
#define FALSE 0
#define BUF_SIZE 1024

char *welcome_info = "\nWelcome to Student Score Management System\n"\
    "0. Exit\n"\
    "1. Add Student\n"\
    "2. Display All Students\n"\
    "3. Find Student by ID\n"\
    "4. Find Student by Name\n"\
    "5. Add Score\n"\
    "6. Display Average Score\n"\
    "7. Display by Score sort\n"\
    "Enter your choice: ";

typedef struct {
    int id; // 学号
    char name[MAX_NAME_LEN]; // 姓名
    float score; // 成绩
} Student;

int student_count = 0; // 学生数量

Student *students; // 学生数组指针
int g_max_student = MAX_STUDENTS;

Student *stu_sys_init(int num)
{
    Student *stu_sys;
    stu_sys = malloc(num * sizeof(Student));
    if (stu_sys == NULL) {
        printf("student system malloc failed!\n");
    }

    return stu_sys;
}

int write_student_info(Student *s)
{
    FILE *fp = fopen(STUDENT_SYSTEM, "a");
    if (fp == NULL) {
        printf("fopen student_system failed!\n");
        return 1;
    }

    fprintf(fp, "%-4d %-10s %-.2f\n", s->id, s->name, s->score);

    fclose(fp);
    return 0;
}

int check_if_student_exsit(int id)
{
    int i;

    for(i = 0; i < student_count; i++) {
        if(students[i].id == id) {
            return 1;
        }
    }

    return 0;
}

void update_student_info(Student s, int need_write)
{
    Student *stu_reinit;
    int old_max_student;

    if(student_count >= g_max_student) {
        old_max_student = g_max_student;
        g_max_student = g_max_student << 1;
        stu_reinit = stu_sys_init(g_max_student);
        if (stu_reinit == NULL) {
            printf("Database is full!\n");
            return;
        }
        memcpy(stu_reinit, students, old_max_student * sizeof(Student));
        free(students);
        students = stu_reinit;
    }

    if (!check_if_student_exsit(s.id)) {
        students[student_count++] = s;
        if (need_write) {
            printf("Student added successfully, all student: %d!\n", student_count);
            write_student_info(&s);
        }
    } else {
        printf("student has in db, do nothing!\n");
    }
}

void add_student()
{
    Student s;

    printf("Enter student ID: ");
    scanf("%d", &s.id);
    printf("Enter student name: ");
    scanf("%s", s.name);
    s.score = 0.0; // 初始成绩设置为0

    update_student_info(s, TRUE);
}

void print_title()
{
    printf("%-4s %-10s %-5s\n", "ID", "Name", "Score");
}

void display_all_students()
{
    int i;

    printf("-------- All students info --------\n");
    if (student_count == 0) {
        printf("No students!\n");
    } else {
        print_title();
        for(i = 0; i < student_count; i++) {
            printf("%-4d %-10s %-.2f\n", students[i].id, students[i].name, students[i].score);
        }
    }
    printf("--------      End       -----------\n");
}

/**
 * @arr: 有序数组
 * @l: 待查找区间左端点
 * @r: 待查找区间右端点
 * @target: 需要查找的元素
 */
int binary_search(Student *s, int l, int r, int target)
{
    int m;

    while (l <= r) {
        // 计算中间位置
        m = l + (r - l) / 2; // 防止(l+r)直接相加导致的溢出

        // 检查x是否存在于中间位置
        if (s[m].id == target)
            return m;

        // 若x大,则忽略左半部分
        if (s[m].id < target) {
            l = m + 1;
        } else {
            // 若x小,则忽略右半部分
            r = m - 1;
        }
    }

    // 若未找到元素,返回-1
    return -1;
}

void find_student_by_id()
{
    int id, ret;

    printf("Enter student ID to search: ");
    scanf("%d", &id);

    ret = binary_search(students, 0, student_count, id);
    if (ret != -1) {
        print_title();
        printf("%-4d %-10s %-.2f\n", students[ret].id, students[ret].name, students[ret].score);
        return;
    }

    printf("Student with ID %d not found!\n", id);
}

void find_student_by_name()
{
    int i, is_find = 0;
    char name[MAX_NAME_LEN];

    printf("Enter student name to search: ");
    scanf("%s", name);

    for(i = 0; i < student_count; i++) {
        if(strcmp(students[i].name, name) == 0) {
            print_title();
            printf("%-4d %-10s %-.2f\n", students[i].id, students[i].name, students[i].score);
            is_find = 1;
        }
    }

    if (is_find == 0) {
        printf("Student with name %s not found!\n", name);
    }
}

void add_score()
{
    int id, i;
    float score;

    printf("Enter student ID: ");
    scanf("%d", &id);
    printf("Enter student score: ");
    scanf("%f", &score);

    for(i = 0; i < student_count; i++) {
        if(students[i].id == id) {
            students[i].score = score;
            printf("Score added successfully!\n");
            return;
        }
    }
    printf("Student with ID %d not found!\n", id);
}

void display_average_score()
{
    float total = 0.0;
    int i;

    for(i = 0; i < student_count; i++) {
        total += students[i].score;
    }

    printf("Average score of all students: %.2f\n", total / student_count);
}

int init_student_info()
{
    if(access(STUDENT_SYSTEM, F_OK) != 0) { // 文件不存在
        return 0;
    }

    FILE *fp = fopen(STUDENT_SYSTEM, "r");
    if (fp == NULL) {
        printf("fopen student_system failed!\n");
        return 1;
    }

#define BUF_SIZE 1024
    char buf[BUF_SIZE];
    Student s;
    while(fgets(buf, BUF_SIZE - 1, fp) != NULL) {
        sscanf(buf, "%d %s %f\n", &s.id, s.name, &s.score);
        update_student_info(s, FALSE);
    }

    fclose(fp);
    return 0;

}

void swap(Student *a, Student *b)
{
    Student tmp = *a;
    *a = *b;
    *b = tmp;
}

void bubble_sort_by_score(Student *s, int n)
{
    int i, j;

    for (i = 0; i < n-1; i++) {
        for (j = 0; j < n-i-1; j++) { // 最后 i 个已经排序好了, 遍历未排序的部分
            if (s[j].score < s[j+1].score) {
                // 如果当前元素大于后面的元素,交换它们
                swap(&s[j], &s[j+1]);
            }
        }
    }
}

int do_process(int choice)
{
    switch(choice) {
        case 0:
            printf("Exiting...\n");
            break;
        case 1:
            add_student();
            break;
        case 2:
            display_all_students();
            break;
        case 3:
            find_student_by_id();
            break;
        case 4:
            find_student_by_name();
            break;
        case 5:
            add_score();
            break;
        case 6:
            display_average_score();
            break;
        case 7:
            bubble_sort_by_score(students, student_count);
            display_all_students();
            break;
        default:
            printf("Invalid choice!\n");
    }

    return 0;
}

int do_exit(int fd)
{
    int ret;
    char *exit_instruct = "Exit";

    ret = send(fd, exit_instruct, strlen(exit_instruct), 0);
    if (ret == -1) {
        perror("sent failed!\n");
        return -1;
    }
    printf("send: %s\n", exit_instruct);

    return 0;
}

int do_add_student(int fd)
{
    int ret;
    char *enter_id_helps = "Enter student ID:";
    char *name_helps = "Enter student name:";
    Student s;
    char buf[BUF_SIZE];

    ret = send(fd, enter_id_helps, strlen(enter_id_helps), 0);
    if (ret == -1) {
        perror("sent failed!\n");
        return -1;
    }
    printf("send: %s\n", enter_id_helps);

    // Read data from the socket
    ret = read(fd, buf, BUF_SIZE);
    if (ret == -1) {
        perror("read failed!\n");
        return -1;
    }

    s.id = atoi(buf);

    ret = send(fd, name_helps, strlen(name_helps), 0);
    if (ret == -1) {
        perror("sent failed!\n");
        return -1;
    }

    // Read data from the socket
    memset(buf, 0, BUF_SIZE);
    ret = read(fd, buf, BUF_SIZE);
    if (ret == -1) {
        perror("read failed!\n");
        return -1;
    }

    strcpy(s.name, buf);
    s.score = 0.0; // 初始成绩设置为0

    update_student_info(s, TRUE);
    return 0;
}

int server_process_to_client(int fd, char *buffer)
{
    int choice = atoi(buffer);
    switch(choice) {
        case 0:
            do_exit(fd);
            return 1;
        case 1:
            do_add_student(fd);
            break;
        default:
            printf("Invalid choice!\n");
    }

    return 0;
}

void *do_data_process(void *arg)
{
    int ret;
    char buffer[BUF_SIZE] = {0};
    int conn_fd = *(int *)arg;

    while (1) {
        // Send data to the client
        ret = send(conn_fd, welcome_info, strlen(welcome_info), 0);
        if (ret == -1) {
            perror("sent failed!\n");
            break;
        }

        // Read data from the socket
        memset(buffer, 0, BUF_SIZE);
        ret = read(conn_fd, buffer, BUF_SIZE);
        if (ret == -1) {
            perror("read failed!\n");
            break;
        }

        printf("recv: %s, %d\n", buffer, strlen(buffer));
        ret = server_process_to_client(conn_fd, buffer);
        if (ret == 1) {
            break;
        }
    }

    close(conn_fd);
    return NULL;
}

int socket_server_init()
{
    int ret;
    int server_fd, conn_fd;
    int opt = 1;
    struct sockaddr_in address;
    int addrlen = sizeof(address);
    pthread_t thread;

    // 创建 TCP socket
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        perror("socket failed!");
        exit(EXIT_FAILURE);
    }

    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
        perror("setsockopt!");
        exit(EXIT_FAILURE);
    }

#define PORT 8080
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(PORT);

    // Bind the socket to the address
    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
        perror("bind failed!");
        exit(EXIT_FAILURE);
    }

    // Listen for connections
    if (listen(server_fd, 3) < 0) {
        perror("listen!");
        exit(EXIT_FAILURE);
    }

    while (1) {
        // Accept a connection
        if ((conn_fd = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
            perror("accept");
            exit(EXIT_FAILURE);
        }

        // 启动线程去处理数据
        ret = pthread_create(&thread, NULL, do_data_process, &conn_fd);
        if (ret) {
            printf("pthread_create failed, ret:%d\n", ret);
            exit(EXIT_FAILURE);
        }
    }

    close(server_fd);
    return 0;
}

void welcome_sys_menu()
{
    printf("%s", welcome_info);
}

int main()
{
    int choice;
    int ret;

    students = stu_sys_init(MAX_STUDENTS);
    if (students == NULL) {
        printf("student system init failed, exit!\n");
        return -1;
    }

    ret = init_student_info();
    if (ret) {
        printf("init_student_info failed!\n");
        return 1;
    }
    display_all_students();

    socket_server_init();

    do {
        welcome_sys_menu();
        scanf("%d", &choice);

        do_process(choice);
    } while(choice != 0);

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值