【Linux】线程封装

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

一、为什么需要封装线程库?

pthread的痛点:

封装带来的好处:

二、线程封装核心代码解析

[1. 头文件定义(Thread.hpp)](#1. 头文件定义(Thread.hpp))

三、关键技术点详解

[1. std::function的魅力](#1. std::function的魅力)

[2. 静态成员函数的巧妙使用](#2. 静态成员函数的巧妙使用)

[3. 获取真实的线程ID](#3. 获取真实的线程ID)

四、使用示例和测试代码

测试代码(main.cpp)


一、为什么需要封装线程库?

在Linux C++开发中,我们经常需要使用多线程。原生的pthread接口虽然强大,但存在一些问题:

pthread的痛点:

  • 🔧 C风格接口:函数指针和void*参数不够类型安全

  • 📝 冗长的代码:需要手动管理线程创建、连接、销毁

  • 🚫 易出错:容易忘记检查返回值,导致难以调试的问题

  • 🔄 缺乏RAII:资源管理需要手动处理

封装带来的好处:

  • 🎯 类型安全:使用std::function代替函数指针

  • 🚀 简洁易用:几行代码完成线程管理

  • 🛡️ 异常安全:利用RAII自动管理资源

  • 📦 可扩展性:方便添加新功能(如线程池)

二、线程封装核心代码解析

1. 头文件定义(Thread.hpp)

复制代码
#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <vector>
#include <functional>
#include <sys/syscall.h>

// 获取轻量级进程ID(线程ID)
#define get_lwp_id() syscall(SYS_gettid)

// 定义函数类型别名
using func_t = std::function<void()>;

class Thread
{
public:
    // 构造函数:接收线程函数和线程名
    Thread(func_t func, const std::string &name)
        : _name(name), _func(func), _isrunning(false)
    {
    }

    // 静态成员函数:线程启动例程
    static void *start_routine(void *args)
    {
        Thread *self = static_cast<Thread *>(args);
        self->_isrunning = true;
        self->_lwpid = get_lwp_id();  // 获取实际线程ID
        self->_func();                // 执行用户函数
        pthread_exit((void *)0);      // 线程退出
    }

    // 启动线程
    void Start()
    {
        int n = pthread_create(&_tid, nullptr, start_routine, this);
        if (n == 0)
        {
            std::cout << "线程" << _name << "创建成功" << std::endl;
        }
    }

    // 等待线程结束
    void Join()
    {
        if (!_isrunning) return;
        
        int n = pthread_join(_tid, nullptr);
        if (n == 0)
        {
            std::cout << "线程" << _name << "回收成功" << std::endl;
        }
    }

    ~Thread() {}

private:
    bool _isrunning;        // 线程运行状态
    pthread_t _tid;         // 线程ID
    pid_t _lwpid;           // 轻量级进程ID
    std::string _name;      // 线程名称
    func_t _func;           // 线程执行函数
};

三、关键技术点详解

1. std::function的魅力

传统pthread的问题:

复制代码
// C风格:需要静态函数和void*参数
void* thread_func(void* arg) {
    // 需要类型转换
    int* data = (int*)arg;
    // ...
}

我们的解决方案:

复制代码
// C++11风格:类型安全的函数对象
using func_t = std::function<void()>;

// 可以接收任何可调用对象:
// 1. 普通函数
// 2. Lambda表达式  
// 3. 函数对象
// 4. std::bind表达式

2. 静态成员函数的巧妙使用

为什么需要静态函数?

pthread_create要求C风格的函数指针,但普通成员函数有隐式的this参数。

解决方案:

复制代码
static void *start_routine(void *args) {
    Thread *self = static_cast<Thread *>(args);  // 转换回对象指针
    self->_func();  // 调用真正的线程函数
}

3. 获取真实的线程ID

复制代码
#define get_lwp_id() syscall(SYS_gettid)

// 为什么需要这个?
// - pthread_t是进程内标识,在不同进程中可能重复
// - 通过系统调用获取的LWP ID是系统范围内唯一的
// - 便于调试和系统监控

四、使用示例和测试代码

测试代码(main.cpp)

复制代码
#include "Thread.hpp"
#include <iostream>
#include <vector>

// 测试函数
void Test()
{
    int cnt = 3;
    while (cnt--)
    {
        std::cout << "线程" << get_lwp_id() << "正在运行..." << std::endl;
        sleep(1);
    }
}

// Lambda表达式测试
auto lambda_test = []() {
    std::cout << "Lambda线程运行中" << std::endl;
    sleep(2);
};

int main()
{
    std::cout << "=== 单线程测试 ===" << std::endl;
    Thread t1(Test, "single-thread");
    t1.Start();
    t1.Join();

    std::cout << "\n=== 多线程测试 ===" << std::endl;
    std::vector<Thread> threads;
    
    // 创建3个线程
    for (int i = 0; i < 3; i++)
    {
        std::string name = "thread-";
        name += std::to_string(i + 1);
        threads.emplace_back(Test, name);
    }

    // 启动所有线程
    for (auto &thread : threads)
    {
        thread.Start();
    }

    // 等待所有线程结束
    for (auto &thread : threads)
    {
        thread.Join();
    }

    std::cout << "\n=== Lambda测试 ===" << std::endl;
    Thread t2(lambda_test, "lambda-thread");
    t2.Start();
    t2.Join();

    return 0;
}
相关推荐
hope_wisdom2 分钟前
C/C++数据结构之树
数据结构·c++·二叉树·
Run_Teenage3 分钟前
Linux:进程间通信-System V 共享内存
linux·运维·服务器
木子欢儿7 分钟前
Ubuntu 24.04 执行超微服务器 JNLP 程序
linux·运维·服务器·ubuntu
W230357657320 分钟前
经典算法:打家劫舍(动态规划 + 回溯求最优解)C++ 超详细解析
c++·算法·动态规划
凤山老林2 小时前
04-Java JDK, JRE和JVM
java·开发语言·jvm
小成202303202658 小时前
Linux高级02
linux·开发语言
mounter6258 小时前
【硬核前沿】CXL 深度解析:重塑数据中心架构的“高速公路”,Linux 内核如何应对挑战?-- CXL 协议详解与 LSF/MM 最新动态
linux·服务器·网络·架构·kernel
++==9 小时前
Linux 进程间通信与线程同步技术详解:IPC 机制、线程 API、同步工具与经典同步问题
linux
特长腿特长9 小时前
centos、ubantu系列机的用户和用户组的结构是什么?具体怎么配置?用户组权限怎么使用?这篇文章持续更新,帮助你复习linux的基础知识
linux·运维·centos
澈2079 小时前
深入浅出C++滑动窗口算法:原理、实现与实战应用详解
数据结构·c++·算法