为什么有协程这个东西因为要用同步的编程方式实现异步的性能。一、同步是什么async_dns_client_noblock.c 是一个用来把dns域名解析为ip的程序。在这个程序中int main(int argc, char *argv[]) { //tong bu //dns_client_commit(argv[1]); int count sizeof(domain) / sizeof(domain[0]); int i 0; for (i 0;i count;i ) { dns_client_commit(domain[i]); } }dns_client_commit(domain[i]);这个函数会阻塞一直卡到DNS查询完成后返回结果。如果第十个域名匹配成功则会浪费10倍的查询时间。同步是在同一个线程中既负责检查事件就绪又负责读写io。二、异步是什么int main(int argc, char *argv[]) { struct async_context *ctx dns_async_client_init(); if (ctx NULL) return -2; int count sizeof(domain) / sizeof(domain[0]); int i 0; for (i 0;i count;i ) { dns_async_client_commit(ctx, domain[i], dns_async_client_result_callback); //sleep(2); } getchar(); }dns_async_client_init();会创建epoll负责监听就绪事件。分配并初始化异步上下文在堆上分配一块零初始化的内存用于存放async_context结构体将epollfd存进去。创建一个新线程这个线程专门执行dns_async_client_proc(ctx)函数主线程负责接受用户请求创建 DNS 查询 socket发送 DNS 请求包然后将 socket 注册到 epoll立即返回继续处理下一个请求。Reactor 线程负责监听所有 socket 的响应一旦收到 DNS 回复就调用回调函数通知用户结合图片1. 初始化阶段主线程 Reactor 线程│ │├─ epoll_create() │├─ 分配 ctx │├─ 创建 Reactor 线程 ──────────→ ││ ├─ epoll_wait() 阻塞等待├─ usleep(1) │ 没有事件阻塞住│ 让出 CPU 给子线程 │2. 提交异步请求阶段dns_async_client_commit主线程每次调用 dns_async_client_commit│├─ 创建 UDP socket├─ 构造 DNS 查询包├─ sendto() 发送请求非阻塞立即返回├─ 将 socket 注册到 ctx-epfd│ epoll_ctl(ADD, EPOLLIN)├─ 保存回调函数到某个数据结构└─ 立即返回不等待结果3. Reactor 线程处理完成事件Reactor 线程循环│├─ epoll_wait() 返回就绪的 socket├─ 找出这个 socket 对应的回调函数├─ 调用 recvfrom() 读取 DNS 响应├─ 解析 DNS 响应包提取 IP 地址├─ 调用回调函数 dns_async_client_result_callback(domain, result)│ 通知用户DNS 查询完成├─ 清理 socket 和上下文└─ 继续下一轮 epoll_wait()实际测试中同步解析耗时1.575s 异步耗时0.17s。异步显然比同步要快。server mul port .c是一份用来接收百万并发连接的tcp服务器。在该程序中异步是开辟一块线程池封装成job扔到线程池。epoll只负责检查是否就绪。读写io的recv和send做成一个任务io_task, 交给其他线程。每创建1000个端口所用时间1593ms。同步则是在同一个线程中既负责检查事件就绪又负责读写io。每创建1000个端口所用时间7300ms显然异步同样比同步要快。三、协程是什么可是异步在编程的时候判断当前接口是否可操作不可就跳转到下一个。来回飞雷神导致代码及其看不懂。所以把“保存上下文”、“切换”封装起来让人感觉不到这就是用同步的方式实现异步的性能。四、协程在代码中是如何实现的while (1) { char buffer[128] {0}; int count read(clientfd, buffer, 128); if (count 0) { break; } write(clientfd, buffer, count); printf(sockfd: %d, clientfd: %d, count: %d, buffer: %s\n, sockfd, clientfd, count, buffer); } ssize_t read(int fd, void *buf, size_t count) { struct pollfd fds[1] {0}; fds[0].fd fd; fds[0].events POLLIN; int res poll(fds, 1, 0); if (res 0) { // // fd -- epoll_ctl(); swapcontext(); // fd -- ctx }在代码中重写了一个read。当调用read是会执行自己写好的read。先把fd放到poll里面。做一个判断如果可读则去读。如果不可读把fd加入epoll里面监听可读事件。调用swapcontext保留信息跳转到其他协程里面。如果可读之后则会加入epoll就绪队列切换回当前的协程继续处理。