线程池 / I/O复用 / 协程 简要备忘录
目录
线程池(Thread Pool)
I/O 复用(I/O Multiplexing)
协程(Coroutine)
一、线程池(C实现思路)
核心结构
示例代码(简化线程池)
特点
二、I/O 复用(select / epoll)
示例:多连接监听
特点
三、协程(C语言模拟版本)
协程思想(核心)
示例(状态机版协程)
特点
四、三者对比(C实现视角)
五、最重要的系统级理解
线程池(Thread Pool)
解决:如何高效管理“任务执行线程”
- 预先创建线程
- 任务来了就分配
- 避免频繁创建/销毁线程
适合:CPU任务 / 业务处理
I/O 复用(I/O Multiplexing)
I/O复用
解决:一个线程如何管理多个连接
- 用 select / epoll 监听多个 socket
- 哪个有事件就处理哪个
- 不阻塞等待
协程(Coroutine)
解决:一个线程如何执行多个“可暂停任务”
- 用户态调度
- 遇到 IO 自动挂起
- 恢复时继续执行
适合:高并发 IO(如爬虫、服务端)
一、线程池(C实现思路)
核心结构
- 任务队列(queue)
- worker线程(固定数量)
- 互斥锁 + 条件变量
示例代码(简化线程池)
#include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #define THREAD_NUM 3 #define TASK_NUM 10 typedef struct { int task_id; } task_t; task_t task_queue[TASK_NUM]; int task_count = 0; pthread_mutex_t mutex; pthread_cond_t cond; void* worker(void* arg) { int id = *(int*)arg; while (1) { pthread_mutex_lock(&mutex); while (task_count == 0) { pthread_cond_wait(&cond, &mutex); } task_t task = task_queue[--task_count]; pthread_mutex_unlock(&mutex); printf("线程 %d 处理任务 %d\n", id, task.task_id); sleep(1); } return NULL; } int main() { pthread_t threads[THREAD_NUM]; int ids[THREAD_NUM]; pthread_mutex_init(&mutex, NULL); pthread_cond_init(&cond, NULL); for (int i = 0; i < THREAD_NUM; i++) { ids[i] = i; pthread_create(&threads[i], NULL, worker, &ids[i]); } // 投递任务 for (int i = 0; i < TASK_NUM; i++) { pthread_mutex_lock(&mutex); task_queue[task_count++].task_id = i; pthread_mutex_unlock(&mutex); pthread_cond_signal(&cond); } sleep(10); return 0; }特点
- 多线程并行
- 线程复用
- 适合 CPU/业务任务
二、I/O 复用(select / epoll)
I/O复用
这里用select版本(最经典)
示例:多连接监听
#include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/select.h> #include <sys/socket.h> #include <arpa/inet.h> int main() { int server_fd = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = htons(8080); bind(server_fd, (struct sockaddr*)&addr, sizeof(addr)); listen(server_fd, 5); fd_set readfds; int max_fd = server_fd; while (1) { FD_ZERO(&readfds); FD_SET(server_fd, &readfds); int ret = select(max_fd + 1, &readfds, NULL, NULL, NULL); if (FD_ISSET(server_fd, &readfds)) { int client_fd = accept(server_fd, NULL, NULL); printf("新连接:%d\n", client_fd); } } return 0; }特点
- 单线程管理多个连接
- 事件触发处理
- 避免线程爆炸
三、协程(C语言模拟版本)
C语言没有原生协程
但可以用:
- setjmp / longjmp
- 状态机
- 或 ucontext(已废弃但经典)
协程思想(核心)
“函数可以暂停,下次从上次位置继续”
示例(状态机版协程)
#include <stdio.h> typedef struct { int state; } coroutine_t; void task(coroutine_t* co) { switch (co->state) { case 0: printf("任务开始\n"); co->state = 1; return; case 1: printf("处理中...\n"); co->state = 2; return; case 2: printf("任务结束\n"); co->state = -1; return; } } int main() { coroutine_t co = {0}; while (co.state != -1) { task(&co); } return 0; }特点
- 单线程
- 手动保存状态
- 模拟“挂起/恢复”
- 本质是用户态调度
四、三者对比(C实现视角)
| 模型 | 核心机制 | 是否多线程 | 是否阻塞 | 适用场景 |
|---|---|---|---|---|
| 线程池 | pthread | 是 | 可能阻塞 | CPU任务 |
| I/O复用 | select/epoll | 否 | 不阻塞 | 网络服务器 |
| 协程 | 状态机模拟 | 否 | 非阻塞 | 高并发IO |
五、最重要的系统级理解
一个现代服务器通常是:
epoll(I/O复用) + 线程池 + 协程思想
例如:
- nginx(epoll + 事件驱动)
- redis(单线程 + IO复用)
- Go runtime(协程调度器)