18. 高并发模型.md 10 KB

18. 高并发模型

一、概述

高并发模型是服务器处理大量客户端连接的核心架构。Linux下主要有以下几种经典模型:

二、fork多进程模型

2.1 原理

每个新连接fork一个子进程处理,父进程继续监听。

2.2 API详解

#include <unistd.h>
pid_t fork(void);
  • 返回值:父进程返回子进程PID,子进程返回0,失败返回-1

2.3 代码示例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <signal.h>
#include <sys/wait.h>

void handle_client(int client_fd) {
    char buf[1024];
    ssize_t n;
    while ((n = recv(client_fd, buf, sizeof(buf), 0)) > 0) {
        send(client_fd, buf, n, 0);
    }
    close(client_fd);
    exit(0);
}

int main() {
    int server_fd = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in addr = {
        .sin_family = AF_INET,
        .sin_port = htons(8080),
        .sin_addr.s_addr = INADDR_ANY
    };
    bind(server_fd, (struct sockaddr*)&addr, sizeof(addr));
    listen(server_fd, 128);

    signal(SIGCHLD, SIG_IGN); // 自动回收子进程

    while (1) {
        int client_fd = accept(server_fd, NULL, NULL);
        if (fork() == 0) {
            close(server_fd);
            handle_client(client_fd);
        }
        close(client_fd);
    }
    return 0;
}

编译命令:

gcc -o fork_server fork_server.c
./fork_server

三、多线程模型

3.1 原理

每个连接创建一个新线程处理,共享进程地址空间。

3.2 API详解

#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                   void *(*start_routine)(void*), void *arg);
void pthread_exit(void *retval);

3.3 代码示例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/socket.h>
#include <netinet/in.h>

void* handle_client(void* arg) {
    int client_fd = *(int*)arg;
    free(arg);
    char buf[1024];
    ssize_t n;
    while ((n = recv(client_fd, buf, sizeof(buf), 0)) > 0) {
        send(client_fd, buf, n, 0);
    }
    close(client_fd);
    return NULL;
}

int main() {
    int server_fd = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in addr = {
        .sin_family = AF_INET,
        .sin_port = htons(8080),
        .sin_addr.s_addr = INADDR_ANY
    };
    bind(server_fd, (struct sockaddr*)&addr, sizeof(addr));
    listen(server_fd, 128);

    while (1) {
        int client_fd = accept(server_fd, NULL, NULL);
        int *fd = malloc(sizeof(int));
        *fd = client_fd;
        pthread_t tid;
        pthread_create(&tid, NULL, handle_client, fd);
        pthread_detach(tid);
    }
    return 0;
}

编译命令:

gcc -o thread_server thread_server.c -lpthread
./thread_server

四、线程池模型

4.1 原理

预创建固定数量线程,任务队列分发连接。

4.2 代码示例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define THREAD_POOL_SIZE 4
#define TASK_QUEUE_SIZE 128

typedef struct {
    int client_fd;
} task_t;

typedef struct {
    task_t queue[TASK_QUEUE_SIZE];
    int front, rear, count;
    pthread_mutex_t mutex;
    pthread_cond_t cond;
    pthread_t threads[THREAD_POOL_SIZE];
} thread_pool_t;

void* worker(void* arg) {
    thread_pool_t *pool = (thread_pool_t*)arg;
    while (1) {
        pthread_mutex_lock(&pool->mutex);
        while (pool->count == 0) {
            pthread_cond_wait(&pool->cond, &pool->mutex);
        }
        task_t task = pool->queue[pool->front];
        pool->front = (pool->front + 1) % TASK_QUEUE_SIZE;
        pool->count--;
        pthread_mutex_unlock(&pool->mutex);

        char buf[1024];
        ssize_t n;
        while ((n = recv(task.client_fd, buf, sizeof(buf), 0)) > 0) {
            send(task.client_fd, buf, n, 0);
        }
        close(task.client_fd);
    }
    return NULL;
}

void pool_init(thread_pool_t *pool) {
    pool->front = pool->rear = pool->count = 0;
    pthread_mutex_init(&pool->mutex, NULL);
    pthread_cond_init(&pool->cond, NULL);
    for (int i = 0; i < THREAD_POOL_SIZE; i++) {
        pthread_create(&pool->threads[i], NULL, worker, pool);
    }
}

void pool_add_task(thread_pool_t *pool, int client_fd) {
    pthread_mutex_lock(&pool->mutex);
    pool->queue[pool->rear].client_fd = client_fd;
    pool->rear = (pool->rear + 1) % TASK_QUEUE_SIZE;
    pool->count++;
    pthread_cond_signal(&pool->cond);
    pthread_mutex_unlock(&pool->mutex);
}

int main() {
    thread_pool_t pool;
    pool_init(&pool);

    int server_fd = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in addr = {
        .sin_family = AF_INET,
        .sin_port = htons(8080),
        .sin_addr.s_addr = INADDR_ANY
    };
    bind(server_fd, (struct sockaddr*)&addr, sizeof(addr));
    listen(server_fd, 128);

    while (1) {
        int client_fd = accept(server_fd, NULL, NULL);
        pool_add_task(&pool, client_fd);
    }
    return 0;
}

编译命令:

gcc -o thread_pool_server thread_pool_server.c -lpthread
./thread_pool_server

五、IO多路复用+多线程

5.1 原理

epoll监听所有fd,事件触发后分发给工作线程池处理。

5.2 API详解

#include <sys/epoll.h>
int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

5.3 代码示例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/epoll.h>

#define MAX_EVENTS 128
#define THREAD_POOL_SIZE 4

typedef struct {
    int client_fd;
} task_t;

typedef struct {
    task_t queue[128];
    int front, rear, count;
    pthread_mutex_t mutex;
    pthread_cond_t cond;
    pthread_t threads[THREAD_POOL_SIZE];
} thread_pool_t;

void* worker(void* arg) {
    thread_pool_t *pool = (thread_pool_t*)arg;
    while (1) {
        pthread_mutex_lock(&pool->mutex);
        while (pool->count == 0) {
            pthread_cond_wait(&pool->cond, &pool->mutex);
        }
        task_t task = pool->queue[pool->front];
        pool->front = (pool->front + 1) % 128;
        pool->count--;
        pthread_mutex_unlock(&pool->mutex);

        char buf[1024];
        ssize_t n;
        while ((n = recv(task.client_fd, buf, sizeof(buf), 0)) > 0) {
            send(task.client_fd, buf, n, 0);
        }
        close(task.client_fd);
    }
    return NULL;
}

void pool_init(thread_pool_t *pool) {
    pool->front = pool->rear = pool->count = 0;
    pthread_mutex_init(&pool->mutex, NULL);
    pthread_cond_init(&pool->cond, NULL);
    for (int i = 0; i < THREAD_POOL_SIZE; i++) {
        pthread_create(&pool->threads[i], NULL, worker, pool);
    }
}

void pool_add_task(thread_pool_t *pool, int client_fd) {
    pthread_mutex_lock(&pool->mutex);
    pool->queue[pool->rear].client_fd = client_fd;
    pool->rear = (pool->rear + 1) % 128;
    pool->count++;
    pthread_cond_signal(&pool->cond);
    pthread_mutex_unlock(&pool->mutex);
}

int main() {
    thread_pool_t pool;
    pool_init(&pool);

    int server_fd = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in addr = {
        .sin_family = AF_INET,
        .sin_port = htons(8080),
        .sin_addr.s_addr = INADDR_ANY
    };
    bind(server_fd, (struct sockaddr*)&addr, sizeof(addr));
    listen(server_fd, 128);

    int epfd = epoll_create1(0);
    struct epoll_event ev = {
        .events = EPOLLIN,
        .data.fd = server_fd
    };
    epoll_ctl(epfd, EPOLL_CTL_ADD, server_fd, &ev);

    struct epoll_event events[MAX_EVENTS];
    while (1) {
        int nfds = epoll_wait(epfd, events, MAX_EVENTS, -1);
        for (int i = 0; i < nfds; i++) {
            if (events[i].data.fd == server_fd) {
                int client_fd = accept(server_fd, NULL, NULL);
                ev.events = EPOLLIN;
                ev.data.fd = client_fd;
                epoll_ctl(epfd, EPOLL_CTL_ADD, client_fd, &ev);
            } else {
                epoll_ctl(epfd, EPOLL_CTL_DEL, events[i].data.fd, NULL);
                pool_add_task(&pool, events[i].data.fd);
            }
        }
    }
    return 0;
}

编译命令:

gcc -o epoll_thread_server epoll_thread_server.c -lpthread
./epoll_thread_server

六、各模型对比

模型 资源占用 并发数 实现复杂度 适用场景
fork多进程 高(独立地址空间) 低(进程数限制) 小型服务、连接数少
多线程 中(共享地址空间) 中(线程数限制) 中等并发、CPU密集
线程池 中(复用线程) 中高 连接数多但处理快
IO多路复用+多线程 低(事件驱动) 高(万级连接) 高并发、IO密集

七、嵌入式选型建议

  1. 资源受限设备(RAM<64MB):线程池模型,避免fork开销
  2. 实时性要求高:IO多路复用+专用处理线程
  3. 简单控制逻辑:fork模型,代码简单易维护
  4. 网络设备(路由器/交换机):epoll+线程池,高并发低延迟
  5. 音频/视频流:IO多路复用+环形缓冲区,避免拷贝

八、注意事项

  1. fork后注意文件描述符继承和清理
  2. 线程间共享数据需加锁保护
  3. 线程池任务队列满时的处理策略
  4. epoll的ET和LT模式选择
  5. 信号处理与线程安全的冲突

九、面试要点

  1. fork与线程的区别:地址空间、文件描述符、通信方式
  2. epoll的ET/LT区别:边缘触发需一次性读完,水平触发缓冲区空则通知
  3. 线程池为何比多线程高效:避免频繁创建/销毁线程开销
  4. 高并发服务器的设计模式:Reactor、Proactor模式
  5. 如何处理C10K问题:IO多路复用+非阻塞IO