关于hiredis异步:能否在其他线程调用event_base_dispatch(base)?
关于hiredis多线程分离命令发送与event循环的实现
当然可以这么做!这种把异步Redis命令提交和libevent事件循环拆到不同线程的模式,在hiredis异步场景里太常用了——特别适合主线程要处理业务逻辑,不想被阻塞的事件循环占住的情况。
先划几个关键注意事项
- 首先要理清线程边界:
event_base的创建、和Redis异步上下文的绑定(也就是redisLibeventAttach()),以及后续的事件循环(event_base_dispatch()),必须保证只有事件循环线程在操作这个event_base(除了event_base_loopexit()这种线程安全的控制函数)。- 父线程单独调用
redisAsyncCommand()是安全的,但如果有多个线程都要发命令,一定要加互斥锁保护redisAsyncContext——hiredis的异步上下文本身不是线程安全的,多线程直接写会出问题。
- 务必在发送任何命令之前,完成
redisLibeventAttach()的绑定操作,不然你的命令根本不会被事件循环处理。
给你个完整的示例代码
1. 事件循环的线程函数
#include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <hiredis/hiredis.h> #include <hiredis/async.h> #include <hiredis/adapters/libevent.h> // 这里为了示例简单用全局变量,实际项目建议用结构体封装传参 struct event_base *global_base; redisAsyncContext *global_redis_ctx; void* event_loop_worker(void *arg) { printf("Event loop thread started, running dispatch...\n"); // 阻塞跑事件循环,直到调用event_base_loopexit()或者所有事件处理完 event_base_dispatch(global_base); printf("Event loop exited cleanly\n"); return NULL; }
2. Redis命令的回调函数
void redis_reply_callback(redisAsyncContext *ctx, void *reply, void *privdata) { if (reply == NULL) { fprintf(stderr, "Command failed: %s\n", ctx->errstr); return; } // 把回复转成字符串打印,实际业务里可以按需处理 printf("Got reply: %s\n", redisReplyToString(reply)); // 注意:异步模式下hiredis会自动回收reply,不用手动free! }
3. 主函数(父线程逻辑)
int main() { // 创建event_base global_base = event_base_new(); if (!global_base) { fprintf(stderr, "Failed to create event base\n"); exit(EXIT_FAILURE); } // 建立异步Redis连接 global_redis_ctx = redisAsyncConnect("127.0.0.1", 6379); if (global_redis_ctx->err) { fprintf(stderr, "Connection error: %s\n", global_redis_ctx->errstr); redisAsyncFree(global_redis_ctx); event_base_free(global_base); exit(EXIT_FAILURE); } // 把Redis异步上下文绑定到event_base redisLibeventAttach(global_redis_ctx, global_base); // 创建线程跑事件循环 pthread_t loop_tid; if (pthread_create(&loop_tid, NULL, event_loop_worker, NULL) != 0) { fprintf(stderr, "Failed to spawn event loop thread\n"); redisAsyncFree(global_redis_ctx); event_base_free(global_base); exit(EXIT_FAILURE); } // 父线程批量发送Redis命令 printf("Sending commands from main thread...\n"); redisAsyncCommand(global_redis_ctx, redis_reply_callback, NULL, "SET username alice"); redisAsyncCommand(global_redis_ctx, redis_reply_callback, NULL, "GET username"); redisAsyncCommand(global_redis_ctx, redis_reply_callback, NULL, "INCR login_count"); // 这里可以处理你的其他业务逻辑,比如等待用户输入、处理其他任务 sleep(2); // 优雅停止事件循环,然后等待线程结束 event_base_loopexit(global_base, NULL); pthread_join(loop_tid, NULL); // 清理资源 redisAsyncFree(global_redis_ctx); event_base_free(global_base); return EXIT_SUCCESS; }
最后再敲几个重点
- 事件循环线程只负责跑
event_base_dispatch(),别在这个线程里做其他耗时操作,不然会阻塞IO处理。 - 如果需要多线程发命令,一定要给
redisAsyncContext加锁——比如用pthread_mutex_t包裹所有redisAsyncCommand()的调用。 - 退出时一定要先停止事件循环,再等待线程结束,最后清理资源,别直接杀线程或者漏释放,容易搞出内存泄漏或者崩溃。
内容的提问来源于stack exchange,提问作者Raja Narayan
相关产品推荐
相关产品推荐

