线程池 / I/O复用 / 协程 简要备忘录

📅 2026/7/7 3:24:28 👁️ 阅读次数 📝 编程学习
线程池 / 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(协程调度器)