You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

关于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 12:03:43