libcurl多线程与libevent驱动不可混用:前者为阻塞I/O+pthread,后者为单线程非阻塞事件循环;必须通过curl_multi_*接口配合socket事件通知,动态管理libevent的event实例,并严格同步socket生命周期与HTTP/2/TLS事件。

libcurl 多线程 vs libevent 驱动:别混用 pthread + event loop
直接用 pthread 创建多个线程跑 curl_easy_perform(),和用 libevent 驱动 libcurl 的异步接口(curl_multi_*)是两套完全不同的模型。前者是阻塞 I/O + 线程并发,后者是单线程事件循环 + 非阻塞 I/O。强行在线程里嵌套 libevent 主循环或在 libevent 回调里调 curl_easy_perform(),大概率触发死锁或未定义行为。
-
libcurl的异步能力只通过curl_multi_*系列函数暴露,必须配合其内部的 socket 事件通知机制 -
libevent不能直接“接管”curl_easy_handle,只能监听curl_multi_socket_action()返回的 socket fd 变化 - 每个
CURLM句柄需绑定一个独立的libeventstruct event_base *,不可跨 base 共享
curl\_multi\_socket\_action 如何与 libevent 绑定
核心在于把 libcurl 多接口句柄的 socket 状态变化,转为 libevent 可监听的事件。关键步骤不是“注册回调”,而是动态管理 event 实例:
- 调用
curl_multi_socket_action(multi_handle, CURL_SOCKET_TIMEOUT, 0, &still_running)获取超时值,用event_add()设置定时器 - 当
curl_multi_socket_action()返回新 socket fd 或状态变更,用event_del()清旧event,再用event_new()+event_add()注册新监听(EV_READ/EV_WRITE) - libevent 回调中必须再次调用
curl_multi_socket_action(),否则 curl 内部状态不同步
static void curl_perform_cb(int fd, short kind, void *userp) {
CURLM *multi = (CURLM *)userp;
int action = (kind & EV_READ) ? CURL_CSELECT_IN : CURL_CSELECT_OUT;
int running_handles;
curl_multi_socket_action(multi, fd, action, &running_handles);
}
常见崩溃点:socket fd 生命周期错位
libcurl 可能在任意时刻通过 CURLMOPT_SOCKETFUNCTION 回调告诉你:“这个 fd 我不用了,请删掉对应 event”。但如果你在 libevent 回调里刚调完 curl_multi_socket_action() 就立刻 event_free(),而 libcurl 内部还在引用该 fd,就会 crash。
- 必须实现 socket 关闭钩子:
curl_multi_setopt(multi, CURLMOPT_SOCKETFUNCTION, sock_cb) -
sock_cb中收到action == CURL_POLL_REMOVE时,仅标记待清理,**不立即 free event**;等下一次curl_multi_socket_action()调用前统一清理 - libevent 的
event必须用event_set()初始化而非event_new(),避免重复分配内存导致悬挂指针
HTTP/2 和 TLS 握手对事件循环的影响
启用 CURLOPT_HTTP_VERSION = CURL_HTTP_VERSION_2TLS 后,libcurl 可能触发额外的非 socket 事件(如 ALPN 协商、HPACK 解码),这些不会反映在 socket fd 上,但会卡住 curl_multi_perform() 进度。
- 必须设置
curl_multi_setopt(multi, CURLMOPT_TIMERFUNCTION, timer_cb),并在timer_cb中用event_add()更新超时,否则 HTTP/2 流控会失效 - OpenSSL 的
SSL_read()/SSL_write()在非阻塞模式下返回SSL_WANT_READ/SSL_WANT_WRITE时,libcurl 会自动重试,但要求你确保对应 socket 仍在 libevent 监听中 —— 别在SSL_WANT_WRITE时误删EV_READ事件 - 调试时加
curl_multi_setopt(multi, CURLMOPT_VERBOSITY, CURLPIPE_MULTIPLEX)查看是否真走 HTTP/2 多路复用
curl_multi_socket_action() 维系,少一次就可能卡死。










