面向对象封装线程:用 C++ 封装 pthread

引入

本文旨在通过面向对象的方式,将 POSIX 线程(pthread)进行封装,提供一个简洁、安全、易用的 Thread 类接口。核心目标是:隐藏底层 pthread 的复杂性,暴露高层语义清晰的操作方法(如 Start()、Join()、Detach()、Stop())。

main

main.cc

cpp 复制代码
#include "Thread.hpp"
#include <unistd.h>
#include <vector>

using namespace ThreadModlue;

int main()
{
    // std::vector<Thread> threads;
    // for (int i = 0; i < 10; i++)
    // {
    //     threads.emplace_back([]()
    //                          {
    //     while(true)
    //     {
    //         char name[128];
    //         pthread_getname_np(pthread_self(), name, sizeof(name));
    //         std::cout << "我是一个新线程: " << name << std::endl; // 我的线程的名字是什么呀?debug
    //         sleep(1);
    //     } });
    // }
    // for (auto &thread : threads)
    // {
    //     thread.Start();
    // }

    // for (auto &thread : threads)
    // {
    //     thread.Join();
    // }

    Thread t([](){
        while(true)
        {
            char name[128];
            pthread_getname_np(pthread_self(), name, sizeof(name));
            std::cout << "我是一个新线程: " << name << std::endl; // 我的线程的名字是什么呀?debug
            sleep(1);
        }
    });
    t.Start();
    t.Detach();
    sleep(5);

    t.Stop();

    sleep(5);

    t.Join();

    return 0;
}

thread

thread.hpp

cpp 复制代码
#ifndef _THREAD_H_
#define _THREAD_H_

#include <iostream>
#include <string>
#include <pthread.h>
#include <cstdio>
#include <cstring>
#include <functional>

namespace ThreadModlue
{
    static uint32_t number = 1; 

    class Thread
    {
        using func_t = std::function<void()>; 
    private:
        void EnableDetach()
        {
            std::cout << "线程被分离了" << std::endl;
            _isdetach = true;
        }
        void EnableRunning()
        {
            _isrunning = true;
        }
        static void *Routine(void *args) 
        {
            Thread *self = static_cast<Thread *>(args);
            self->EnableRunning();
            if (self->_isdetach)
                self->Detach();
            pthread_setname_np(self->_tid, self->_name.c_str());
            self->_func(); 

            return nullptr;
        }
    public:
        Thread(func_t func)
            : _tid(0),
              _isdetach(false),
              _isrunning(false),
              res(nullptr),
              _func(func)
        {
            _name = "thread-" + std::to_string(number++);
        }
        void Detach()
        {
            if (_isdetach)
                return;
            if (_isrunning)
                pthread_detach(_tid);
            EnableDetach();
        }

        bool Start()
        {
            if (_isrunning)
                return false;
            int n = pthread_create(&_tid, nullptr, Routine, this);
            if (n != 0)
            {
                std::cerr << "create thread error: " << strerror(n) << std::endl;
                return false;
            }
            else
            {
                std::cout << _name << " create success" << std::endl;
                return true;
            }
        }
        bool Stop()
        {
            if (_isrunning)
            {
                int n = pthread_cancel(_tid);
                if (n != 0)
                {
                    std::cerr << "cancel thread error: " << strerror(n) << std::endl;
                    return false;
                }
                else
                {
                    _isrunning = false;
                    std::cout << _name << " stop" << std::endl;
                    return true;
                }
            }
            return false;
        }
        void Join()
        {
            if (_isdetach)
            {
                std::cout << "你的线程已经是分离的了,不能进行join" << std::endl;
                return;
            }
            int n = pthread_join(_tid, &res);
            if (n != 0)
            {
                std::cerr << "create thread error: " << strerror(n) << std::endl;
            }
            else
            {
                std::cout << "join success" << std::endl;
            }
        }
        ~Thread()
        {
        }

    private:
        pthread_t _tid;
        std::string _name;
        bool _isdetach;
        bool _isrunning;
        void *res;
        func_t _func;
    };
}

#endif

makefile

makefile

shell 复制代码
thread:main.cc
	g++ -o $@ $^ -std=c++11 -lpthread
.PHONY:clean
clean:
	rm -f thread

实验结果


解析

一、整体结构与核心设计思路

1. 代码框架
复制代码
头文件保护宏 → 头文件引入 → 命名空间 → 静态全局变量 → Thread类定义(成员函数+成员变量)
  • 命名空间ThreadModlue 用于隔离线程相关代码,避免命名冲突。
  • 核心设计 :通过 std::function<void()> 接收线程执行的任务(回调函数),把 pthread 的创建、分离、等待、取消等操作封装成类的成员函数,对外提供简洁的接口(Start/Stop/Join/Detach)。
2. 核心依赖
  • pthread.h:POSIX 线程库核心头文件,提供线程创建、管理的原生接口。
  • std::function:C++11 特性,用于封装任意可调用对象(函数、lambda、函数对象等),作为线程的执行体,替代传统的函数指针,灵活性更高。

二、核心成员解析

1. 成员变量(私有)
变量名 类型 作用
_tid pthread_t 线程 ID,唯一标识一个线程
_name std::string 线程名称,格式为 thread-数字(数字由静态变量 number 自增生成)
_isdetach bool 标记线程是否为"分离态"(分离态线程无需手动 join,结束后自动释放资源)
_isrunning bool 标记线程是否正在运行
res void* 存储线程退出时的返回值(pthread_join 的第二个参数)
_func func_t 线程要执行的任务(std::function<void()> 类型)
2. 静态全局变量
cpp 复制代码
static uint32_t number = 1; // bug(代码中标记的bug)
  • 作用:为每个线程生成唯一的名称(thread-1、thread-2...)。
  • 问题:static 修饰的全局变量属于编译单元(.o 文件)级别的静态,若这份头文件被多个 .cpp 包含,会生成多个独立的 number 实例,导致线程名称重复(正确做法应改为 static uint32_t number = 1; 放在类内,或用原子变量保证线程安全)。
3. 核心成员函数
(1)构造函数
cpp 复制代码
Thread(func_t func)
    : _tid(0), _isdetach(false), _isrunning(false), res(nullptr), _func(func)
{
    _name = "thread-" + std::to_string(number++);
}
  • 初始化线程的默认状态:非分离、未运行、线程 ID 为 0。
  • 接收线程要执行的任务 func,并自动生成线程名称。
(2)线程启动:Start()
cpp 复制代码
bool Start()
{
    if (_isrunning) return false; // 防止重复启动
    // 核心:调用 pthread_create 创建线程
    int n = pthread_create(&_tid, nullptr, Routine, this);
    if (n != 0) {
        std::cerr << "create thread error: " << strerror(n) << std::endl;
        return false;
    } else {
        std::cout << _name << " create success" << std::endl;
        return true;
    }
}
  • 核心调用:pthread_create(原生 pthread 线程创建接口)。
  • 关键参数:
    • &_tid:输出线程 ID。
    • nullptr:使用默认线程属性。
    • Routine:线程的入口函数(必须是静态成员函数,因为 pthread 不支持非静态成员函数)。
    • this:把当前 Thread 对象的指针传给 Routine,让静态函数能访问类的非静态成员。
(3)线程入口函数:Routine()(静态)
cpp 复制代码
static void *Routine(void *args)
{
    Thread *self = static_cast<Thread *>(args); // 转换为 Thread 对象指针
    self->EnableRunning(); // 标记线程为运行状态
    if (self->_isdetach) self->Detach(); // 如果设置了分离,执行分离操作
    pthread_setname_np(self->_tid, self->_name.c_str()); // 设置线程名称
    self->_func(); // 执行用户传入的任务
    return nullptr;
}
  • 为什么是静态函数?
    非静态成员函数默认带 this 指针(隐藏参数),而 pthread_create 要求线程入口函数只能是 void* (*)(void*) 类型(无隐藏参数),因此必须用静态成员函数。
  • 核心逻辑:把 void* args 转回 Thread 对象指针,然后执行用户任务 _func()
一、先明确核心矛盾:pthread 的"硬性要求"

pthread 库是基于 C 语言实现的,C 语言没有"类"和"成员函数"的概念,所以 pthread_create(创建线程的核心函数)对线程入口函数有严格的签名要求:

cpp 复制代码
// pthread_create 要求的入口函数类型
void* (*start_routine)(void*);

翻译一下这个要求:

  1. 函数的返回值必须是 void*
  2. 函数的参数必须是且只能是一个 void*
  3. 函数必须是普通函数(C 语言层面的函数,没有隐藏参数)

二、为什么非静态成员函数不行?

先看一个关键结论:C++ 的非静态成员函数,会被编译器偷偷加一个隐藏参数 this

假设你把 Routine 写成非静态的:

cpp 复制代码
// 你看到的代码
void* Routine(void* args) { ... }

// 编译器实际编译后的签名(伪代码)
void* Routine(Thread* this, void* args) { ... }

这个函数的参数变成了 2 个this + args),而不是 pthread 要求的 1 个 void* ,签名完全不匹配------pthread 库是 C 实现的,它不知道也无法处理这个隐藏的 this 指针,编译时会直接报错:

复制代码
error: invalid conversion from 'void* (Thread::*)(void*)' to 'void* (*)(void*)'
三、为什么静态成员函数就可以?

静态成员函数的核心特性,刚好解决了上面的问题:

  1. 静态成员函数没有 this 指针
  • 静态成员函数属于,而不是属于某个具体的对象
  • 编译器不会给它加 this 隐藏参数
  • 它的签名可以完全符合 pthread 的要求:void* (*)(void*)
  1. 静态函数如何访问对象的非静态成员?
    静态函数本身不能直接访问 _func_isdetach 等非静态成员(因为没有 this),所以代码里做了一个关键操作:
cpp 复制代码
static void *Routine(void *args)
{
    // 把 pthread_create 传进来的 this 指针(Thread对象地址)转回来
    Thread *self = static_cast<Thread *>(args);
    // 有了 self(等价于 this),就能访问所有非静态成员了
    self->EnableRunning();
    self->_func();
    ...
}
(4)线程分离:Detach()
cpp 复制代码
void Detach()
{
    if (_isdetach) return; // 已分离则直接返回
    if (_isrunning) pthread_detach(_tid); // 线程运行时执行分离
    EnableDetach(); // 标记为分离态
}
  • 分离态作用:线程结束后自动释放资源,无需调用 pthread_join
  • 注意:分离后的线程不能再调用 Join()
(5)线程等待:Join()
cpp 复制代码
void Join()
{
    if (_isdetach) {
        std::cout << "你的线程已经是分离的了,不能进行join" << std::endl;
        return;
    }
    int n = pthread_join(_tid, &res); // 阻塞等待线程结束,获取返回值
    if (n != 0) {
        std::cerr << "create thread error: " << strerror(n) << std::endl;
    } else {
        std::cout << "join success" << std::endl;
    }
}
  • 核心调用:pthread_join(阻塞等待线程退出,回收资源)。
  • 限制:分离态线程无法 join,会直接提示错误。
(6)线程取消:Stop()
cpp 复制代码
bool Stop()
{
    if (_isrunning) {
        int n = pthread_cancel(_tid); // 取消线程(向线程发送取消请求)
        if (n != 0) {
            std::cerr << "create thread error: " << strerror(n) << std::endl;
            return false;
        } else {
            _isrunning = false;
            std::cout << _name << " stop" << std::endl;
            return true;
        }
    }
    return false;
}
  • 核心调用:pthread_cancel(请求线程退出,并非立即终止,需线程到达"取消点"才会退出)。
  • 注意:pthread_cancel 是"请求"而非"强制杀死",如果线程执行的任务中没有取消点(如循环无阻塞),线程可能不会退出。
(7)辅助函数(私有)
  • EnableDetach():设置 _isdetach = true,标记线程为分离态。
  • EnableRunning():设置 _isrunning = true,标记线程为运行态。

相关推荐
菜宾2 小时前
java-seata基础教学
java·开发语言·adb
梦6502 小时前
JavaScript 循环
开发语言·javascript·ecmascript
guygg882 小时前
5G PDSCH信道吞吐量MATLAB仿真实现(含信道生成与解调)
开发语言·5g·matlab
安特尼2 小时前
X 推荐算法分析
算法·机器学习·推荐算法
傻乐u兔2 小时前
C语音初阶————调试实用技巧2
c语言·开发语言
沛沛老爹3 小时前
从Web到AI:行业专属Agent Skills生态系统技术演进实战
java·开发语言·前端·vue.js·人工智能·rag·企业转型
程农3 小时前
基于Java的报名系统
java·开发语言
yugi9878384 小时前
基于字典缩放的属性散射中心参数提取MATLAB仿真程序
开发语言·matlab
罗湖老棍子4 小时前
强迫症冒险家的任务清单:字典序最小拓扑排序
数据结构·算法·图论·拓扑排序