pthread_create(&tid, &a, start_routine, (void *)pt)第三、四个参数的关系

preface

pthread_create(&tid, &a, start_routine, (void *)pt)

第四个参数 `pt` 里可能确实有自己的类方法,
那为什么不直接让线程执行 `pt` 的类方法,
而还要传第三个参数 `start_routine` 呢?

详细版的解释

简单说:

pthread_create 是 C 接口,它只认识一种固定格式的“线程入口函数”。
第三个参数告诉系统:新线程从哪里开始执行。
第四个参数只是传给这个入口函数的一份数据/对象指针,本身不会被自动执行。


1. pthread_create 的本质

函数原型大概是:

int pthread_create(
    pthread_t *thread,
    const pthread_attr_t *attr,
    void *(*start_routine)(void *),
    void *arg
);

第三个参数要求是一个函数指针:

void *(*start_routine)(void *)

也就是说,它必须是这种形式的函数:

void *func(void *arg);

所以:

pthread_create(&tid, &a, start_routine, (void *)pt);

意思是:

新线程启动后,执行:
start_routine((void *)pt);

注意,不是执行 pt,而是执行 start_routine,并把 pt 传进去。


2. 第四个参数只是“参数”,不是“入口”

可以把它理解成普通函数调用:

start_routine((void *)pt);

这里:

  • start_routine 是函数名,也就是要执行的代码入口;
  • (void *)pt 是传进去的参数。

就像你调用:

printf("hello");

你不会说 "hello" 自己会执行。
"hello" 只是被 printf 使用的数据。

同理:

pthread_create(&tid, &a, start_routine, (void *)pt);

里面的 pt 只是传递给 start_routine 的数据。


3. 那 pt 有类方法,为什么不能直接执行?

假设你有一个类:

class MyThread {
public:
    void run() {
        // 真正的线程逻辑
    }
};

然后:

MyThread *pt = new MyThread();

pt 是一个对象指针,它可以调用:

pt->run();

但问题是:pthread_create 需要的是这种函数:

void *func(void *);

而普通类成员函数 MyThread::run 并不是这种函数。


4. 普通类成员函数有隐藏的 this

比如:

pt->run();

看起来没有参数,但编译器内部其实类似:

MyThread::run(pt);

也就是说,普通成员函数隐含带了一个 this 指针。

所以成员函数:

void MyThread::run();

它的真实调用依赖对象:

pt->run();

pthread_create 不能直接理解 C++ 成员函数的这种调用方式。它只知道:

void *func(void *);

所以不能直接这样写:

pthread_create(&tid, &a, pt->run, nullptr); // 错误

也不能直接这样:

pthread_create(&tid, &a, MyThread::run, pt); // 普通成员函数不匹配

5. start_routine 的作用:中转函数 / 包装函数

通常 start_routine 的作用就是把 void * 转回你的类对象,然后调用对象自己的方法。

例如:

class MyThread {
public:
    void run() {
        // 真正在线程中执行的代码
    }
};

void *start_routine(void *arg) 
{
    MyThread *pt = static_cast<MyThread *>(arg);
    pt->run();
    return nullptr;
}

然后创建线程:

MyThread *pt = new MyThread();

pthread_create(&tid, nullptr, start_routine, (void *)pt);

执行流程是:

新线程开始
    ↓
调用 start_routine(pt)
    ↓
在 start_routine里面把 pt 转回 MyThread*
    ↓
调用 pt->run()
    ↓
真正执行类对象的线程逻辑

所以第三个参数不是多余的,它是线程入口。第四个参数只是让这个入口函数知道“要操作哪个对象”。


6. 可以理解成“入口函数”和“业务对象”的区别

第三个参数 start_routine

void *start_routine(void *arg)

负责:

  • 符合 pthread_create 要求的函数格式;
  • 作为新线程的启动入口;
  • 接收 void * 参数;
  • 把参数转换成真实类型;
  • 调用对象自己的方法。

第四个参数 pt

(void *)pt

负责:

  • 告诉 start_routine 操作哪个对象;
  • 保存类对象的状态、成员变量、成员函数等;
  • 作为上下文传入新线程。

7. 为什么系统不能自动调用 pt 的方法?

因为系统不知道你想调用哪个方法。

比如 pt 里面可能有很多方法:

class MyThread {
public:
    void init();
    void run();
    void stop();
    void reset();
    void process();
};

你传了一个 pt 给线程库,线程库怎么知道你想执行:

pt->run();

还是:

pt->process();

还是:

pt->init();

它不知道。

所以必须明确告诉它一个入口函数:

start_routine

然后你在这个入口函数里面决定调用哪个类方法。


8. 如果想更像 C++,可以用静态成员函数

常见写法是把线程入口写成类的 static 方法:

class MyThread {
public:
    void run() {
        // 线程实际逻辑
    }

    static void *threadEntry(void *arg) {
        MyThread *self = static_cast<MyThread *>(arg);
        self->run();
        return nullptr;
    }
};

创建线程:

MyThread *pt = new MyThread();

pthread_create(&tid, nullptr, MyThread::threadEntry, pt);

这里 threadEntry 是静态成员函数,没有隐藏的 this 指针,所以它可以当作普通函数指针传给 pthread_create


9. 总结一句话

pthread_create 的第三个参数是“线程从哪里开始执行”,第四个参数是“传给这个入口函数的数据”。

虽然第四个参数 pt 是对象,里面有类方法,但它本身只是一个地址,不会被线程自动执行。你必须通过第三个参数 start_routine 这个入口函数,把 pt 转回对象,然后显式调用它的方法,例如:

pt->run();

所以典型结构就是:

void *start_routine(void *arg) {
    MyThread *pt = static_cast<MyThread *>(arg);
    pt->run();
    return nullptr;
}

可以把 start_routine 理解成:pthread 世界和 C++ 对象世界之间的桥梁。

二、用一个比喻来解释:

核心区别:数据 vs 执行逻辑

start_routine线程的入口点pt被操作的对象


类比理解

想象你雇了一个工人(新线程) 去操作一台机器(pt对象)

  • pt(第四个参数)= 那台机器,它有自己的各种功能按钮(类方法)
  • start_routine(第三个参数)= 工人的工作流程手册,规定工人要按什么顺序、在什么时机去按哪些按钮

工人不会自动知道该做什么,必须有人告诉他工作流程


技术层面的原因

pthread 是 C 语言的 API,它不认识 C++ 对象、不懂类方法,它只接受一个普通函数指针作为线程入口。所以需要 start_routine 这个"桥梁函数"来:

新线程启动
    ↓
执行 start_routine(void* pt)   ← 线程必须有个"从哪里开始跑"的地方
    ↓
在函数内部:MyClass* obj = (MyClass*)pt;  ← 还原真实类型
    ↓
调用 obj->doWork();  ← 现在才真正调用对象自己的方法

实际代码示意

// 第三个参数:线程入口函数(C风格,必须是普通函数)
void* start_routine(void* arg) {
    // 在这里决定"怎么用"第四个参数
    MyClass* pt = (MyClass*)arg;  // 还原类型

    // 控制执行顺序、加锁、循环等逻辑
    while (pt->isRunning()) {
        pt->processData();    // 调用pt自己的方法
        pt->updateStatus();
        sleep(1);
    }
    return nullptr;
}

一句话总结

参数角色解决的问题
start_routine“怎么做” — 执行逻辑、流程控制线程从哪里开始、按什么顺序做事
pt“对谁做” — 数据和能力的载体操作哪个对象、用哪些数据

pt 虽然有自己的方法,但它不知道自己应该在新线程里按什么顺序、在什么条件下被调用——这正是 start_routine 存在的意义。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值