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

Linux下RPC多线程服务器结果异常及线程安全改造咨询

解决RPC多线程服务器中SVCXPRT句柄线程安全问题

你猜的完全没错,问题确实出在SVCXPRT传输句柄的共享上。正如你查到的文档所说,这个结构内部的解码/编码缓冲区是全局复用的,当多个线程同时操作同一个transp句柄时,后一个请求的结果会直接覆盖前一个的,这就是为什么两个客户端都拿到了60(第二个请求的计算结果)。

问题核心分析

你的ej_prg_1作为RPC框架的服务入口,把同一个transp句柄传给了不同线程。但SVCXPRT的设计从一开始就不是线程安全的——它的缓冲区是单例式的,多个线程同时读写必然会导致数据串扰,这就是结果错误的根源。

解决方案:为每个线程创建独立的传输句柄副本

要解决这个问题,核心思路是避免在多线程间共享同一个SVCXPRT实例。正确的做法是在处理请求前,为当前请求克隆一个独立的传输句柄,让每个线程操作自己的专属副本,彻底隔离数据缓冲区。

具体修改步骤如下:

  1. 在ej_prg_1中调用svc_clone函数克隆当前的transp句柄,得到独立副本。
  2. 将克隆后的句柄传给线程,而非原始的transp。
  3. 在线程处理完请求后,调用svc_destroy销毁克隆的句柄,避免资源泄漏。

修改后的ej_prg_1代码:

static void ej_prg_1(struct svc_req *rqstp, register SVCXPRT *transp) {
    /* 2 threads possible for now */
    pthread_t th[2];
    pthread_attr_t attr[2];
    static int id=0;
    /* Used to pass arguments to "funcionA" */
    struct data_str{
        struct svc_req *rqstp;
        SVCXPRT *transp;
        int id;
    } *data_ptr=(struct data_str*)malloc(sizeof(struct data_str));

    // 克隆传输句柄,创建独立副本
    SVCXPRT *cloned_transp = svc_clone(transp);
    if (cloned_transp == NULL) {
        fprintf(stderr, "Failed to clone SVCXPRT handle\n");
        svcerr_systemerr(transp);
        free(data_ptr);
        return;
    }

    /* Set parameters */
    data_ptr->rqstp = rqstp;
    data_ptr->transp = cloned_transp; // 传递克隆后的句柄
    data_ptr->id = id;

    /* Create thread */
    printf("Creating thread %d\n", id);
    pthread_attr_init(&attr[id]);
    pthread_attr_setdetachstate(&attr[id], PTHREAD_CREATE_DETACHED);
    pthread_create(&th[id], &attr[id], &funcionA, (void *)data_ptr);
    printf("Thread %d created\n", id);
    id=(id+1)%2;
}

修改后的funcionA代码(增加资源清理):

void *funcionA(void *data) {
    /* Structure for parameters */
    struct thr_data{
        struct svc_req *rqstp;
        SVCXPRT *transp;
        int id;
    } *ptr_data;
    union {
        operands add_1_arg;
    } argument;
    union {
        int add_1_res;
    } result;
    bool_t retval;
    xdrproc_t _xdr_argument, _xdr_result;
    bool_t (*local)(char *, void *, struct svc_req *);

    /* Get the parameters */
    ptr_data = (struct thr_data *)data;
    struct svc_req *rqstp = ptr_data-> rqstp;
    register SVCXPRT *transp = ptr_data-> transp;
    printf("Hello thread: %d\n", ptr_data-> id);

    /*Code generated by rpcgen */
    switch (rqstp->rq_proc) {
        case NULLPROC:
            (void) svc_sendreply (transp, (xdrproc_t) xdr_void, (char *)NULL);
            goto cleanup;
        case ADD:
            _xdr_argument = (xdrproc_t) xdr_operands;
            _xdr_result = (xdrproc_t) xdr_int;
            local = (bool_t (*) (char *, void *, struct svc_req *))add_1_svc;
            break;
        default:
            svcerr_noproc (transp);
            goto cleanup;
    }
    memset ((char *)&argument, 0, sizeof (argument));
    if (!svc_getargs (transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) {
        svcerr_decode (transp);
        goto cleanup;
    }
    retval = (bool_t) (*local)((char *)&argument, (void *)&result, rqstp);
    if (retval > 0 && !svc_sendreply(transp, (xdrproc_t) _xdr_result, (char *)&result)) {
        svcerr_systemerr (transp);
    }
    if (!svc_freeargs (transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) {
        fprintf (stderr, "%s", "unable to free arguments");
        exit (1);
    }
    if (!ej_prg_1_freeresult (transp, _xdr_result, (caddr_t) &result)){
        fprintf (stderr, "%s", "unable to free results");
    }
    /* End of rpcgen code */

cleanup:
    // 销毁克隆的传输句柄
    svc_destroy(ptr_data->transp);
    // 释放参数内存
    free(ptr_data);
    printf("Bye thread: %d\n", ptr_data-> id);
    pthread_exit(0);
}

额外注意事项

  • 一定要检查svc_clone的返回值,如果克隆失败,要及时向客户端返回错误并释放已分配的内存。
  • 你提到的-M标志生成的存根,在Linux下确实需要手动处理线程安全问题,因为传统RPC框架的设计没有考虑多线程并发场景,句柄隔离是最直接有效的解决方式。
  • 线程中使用的rqstp通常在请求处理期间是独立的,克隆句柄后不会有线程安全问题,但如果有额外的全局共享数据,也要注意加锁保护。

这样修改后,每个线程都操作自己独立的SVCXPRT句柄,缓冲区不会被交叉覆盖,两个客户端就能拿到各自正确的结果(40和60)了。

内容的提问来源于stack exchange,提问作者Matias

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 03:40:32