当前位置: 首页 > news >正文

基于pthread库 的 线程封装

目录

一、Thread.hpp

二、Main.cc

三、Makefile

四、代码详细讲解

1、整体结构

2、关键部分详解

2.1 Routine 函数 - 静态成员函数与 this 指针

2.2 构造函数

2.3 Start() 方法

2.4 其他重要方法

3、使用示例分析

2. 线程分离

3. 主线程休眠

4. 尝试停止线程

5. 再次休眠与尝试 Join

4、潜在问题和改进建议

5、总结

五、模板化 Thread 类的改动

1、模板化的核心改动

(1) 模板参数 T 的引入

(2) 回调函数和数据的存储

2、关键逻辑改动

(1) Routine 静态成员函数

(2) 构造函数


一、Thread.hpp

#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; // bug - 静态变量用于生成线程名称编号,但可能存在线程安全问题class Thread{using func_t = std::function<void()>; // 定义函数类型,用于线程执行的回调函数private:// 设置线程为分离状态void EnableDetach(){std::cout << "线程被分离了" << std::endl;_isdetach = true;}// 设置线程为运行状态void EnableRunning(){_isrunning = true;}// 静态线程例程函数,作为pthread_create的入口点// 注意:这是类成员函数,默认包含this指针,所以必须是静态的static void *Routine(void *args){Thread *self = static_cast<Thread *>(args); // 将void*参数转换回Thread指针self->EnableRunning(); // 标记线程开始运行if (self->_isdetach)self->Detach(); // 如果设置为分离,则执行分离操作pthread_setname_np(self->_tid, self->_name.c_str()); // 设置线程名称self->_func(); // 执行用户提供的回调函数return nullptr;}// bug - 这里有注释但没有具体说明是什么bugpublic:// 构造函数,接收线程执行函数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;}}// 停止线程(使用pthread_cancel)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;}// 等待线程结束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;        // 线程IDstd::string _name;     // 线程名称bool _isdetach;        // 是否为分离状态bool _isrunning;       // 是否在运行void *res;             // 线程返回值func_t _func;          // 线程执行函数};
}#endif

二、Main.cc

#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; // 我的线程的名字是什么呀?debugsleep(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;
}

三、Makefile

thread:Main.ccg++ -o $@ $^ -std=c++11 -lpthread
.PHONY:clean
clean:rm -f thread

四、代码详细讲解

下面我将详细讲解这个线程封装类 Thread 的实现,重点放在 Routine 函数和 this 指针的处理上。

1、整体结构

这个代码实现了一个简单的线程封装类,使用 C++11 的 std::function 作为线程执行函数的包装,底层使用 POSIX 线程 (pthread) 实现。

2、关键部分详解

2.1 Routine 函数 - 静态成员函数与 this 指针

static void *Routine(void *args) // 属于类内的成员函数,默认包含this指针!
{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;
}

重点讲解

为什么是静态成员函数?

  • POSIX 线程的入口函数必须是普通函数或静态成员函数,不能是非静态成员函数

  • 非静态成员函数隐含 this 指针参数,而 pthread_create 不知道如何传递这个额外的参数

如何访问类成员?

  • 通过 void* args 参数传递 this 指针

  • 在 Start() 方法中调用 pthread_create 时,最后一个参数传递 this

    int n = pthread_create(&_tid, nullptr, Routine, this);
  • 在 Routine 内部将 args 转换回 Thread* 类型:

    Thread *self = static_cast<Thread *>(args);

执行流程

  • 通过 self 指针访问对象成员和方法

  • 标记线程为运行状态 (EnableRunning())

  • 如果线程被设置为分离模式,调用 Detach()

  • 设置线程名称 (pthread_setname_np)

  • 执行用户提供的函数 (self->_func())

2.2 构造函数

Thread(func_t func): _tid(0),_isdetach(false),_isrunning(false),res(nullptr),_func(func)
{_name = "thread-" + std::to_string(number++);
}
  • 初始化线程 ID (_tid)、分离标志 (_isdetach)、运行标志 (_isrunning) 和结果指针 (res)

  • 将用户提供的函数 func 存储在 _func 成员中

  • 为线程生成唯一名称,使用静态变量 number 保证唯一性

2.3 Start() 方法

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;}
}
  • 检查线程是否已经在运行

  • 调用 pthread_create 创建线程,传递 Routine 作为入口函数,this 作为参数

  • 处理创建成功/失败的情况

2.4 其他重要方法

Detach():

void Detach()
{if (_isdetach)return;if (_isrunning)pthread_detach(_tid);EnableDetach();
}
  • 分离线程,使其结束后自动释放资源

  • 只能对运行中的线程调用

Join():

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;}
}
  • 等待线程结束

  • 不能对已分离的线程调用

Stop():

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,这是一种强制终止线程的方式

3、使用示例分析

在 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;sleep(1);} });
}
for (auto &thread : threads)
{thread.Start();
}for (auto &thread : threads)
{thread.Join();
}
  • 创建 10 个线程对象,每个线程执行一个 lambda 函数

  • 启动所有线程

  • 等待所有线程结束(实际上这些线程是无限循环,程序不会正常退出)

        而后面那段被注释掉的代码展示了另一种使用 Thread 类的方式,与前面 vector 批量创建线程的示例形成对比。下面详细分析这段代码的执行流程和潜在问题:

// Thread t([](){
//     while(true)
//     {
//         char name[128];
//         pthread_getname_np(pthread_self(), name, sizeof(name));
//         std::cout << "我是一个新线程: " << name << std::endl;
//         sleep(1);
//     }
// });
// t.Start();
// t.Detach();
// sleep(5);// t.Stop();// sleep(5);// t.Join();

1. 线程创建与启动

Thread t([](){while(true) {char name[128];pthread_getname_np(pthread_self(), name, sizeof(name));std::cout << "我是一个新线程: " << name << std::endl;sleep(1);}
});
t.Start();

行为

  • 创建一个 Thread 对象 t,传入一个无限循环的 lambda 函数作为线程任务。

  • 调用 t.Start() 启动线程,此时:

    • pthread_create 被调用,线程入口函数是 Routine

    • Routine 中会设置线程名称(通过 pthread_setname_np)。

    • 线程开始执行 lambda 函数,每秒打印一次自己的名称。

2. 线程分离
t.Detach();

行为

  • 调用 Detach() 后,线程会被标记为分离状态(_isdetach = true)。

  • 如果线程正在运行(_isrunning == true),会调用 pthread_detach(_tid)

  • 分离线程的效果

    • 线程结束后自动释放资源,无需其他线程调用 Join()

    • 但分离后不能再对线程调用 Join() 或 Stop()(代码中未严格限制,存在隐患)。

3. 主线程休眠
sleep(5);
  • 行为:主线程休眠 5 秒,此时子线程在后台独立运行(因为已分离)。

4. 尝试停止线程
t.Stop();

行为Stop() 内部调用 pthread_cancel(_tid),尝试强制终止线程。

问题

  • 线程已分离:POSIX 标准中,对分离线程调用 pthread_cancel 是未定义行为(可能成功,也可能无效)。

  • 资源泄漏风险:强制取消线程可能导致线程持有的资源(如锁、内存)未正确释放。

  • 代码逻辑矛盾:既然调用了 Detach(),通常意味着不关心线程的结束状态,再调用 Stop() 逻辑上不合理。

5. 再次休眠与尝试 Join
sleep(5);
t.Join();

行为:再次休眠 5 秒后,尝试调用 t.Join()

问题

  • 线程已被分离,Join() 会直接返回并提示“线程已是分离状态”。

  • 即使线程未分离,Stop() 已经取消了线程,Join() 的行为也取决于线程取消时的状态(可能阻塞或报错)。

潜在问题总结:

分离后调用 Stop() 和 Join()

  • 分离线程的设计初衷是让线程自主运行并自动释放资源,通常不需要其他线程干预其生命周期。

  • 代码中同时调用 Detach() 和 Stop()/Join() 逻辑冲突,可能导致未定义行为。

线程取消的不可靠性

  • pthread_cancel 是强制终止线程的手段,不保证线程能清理资源。

  • 更好的方式是通过标志位让线程协作式退出(例如 lambda 中检查 volatile bool 标志)。

资源管理缺失:如果线程未分离也未 JoinThread 对象的析构函数(当前为空)可能导致资源泄漏。

4、潜在问题和改进建议

number 静态变量的问题

  • 注释中标记为 "bug",因为它可能导致线程一起访问共享资源,造成同步与互斥的问题

  • 更好的方式是使用原子操作或线程安全的计数器

资源管理

  • 析构函数为空,没有清理资源

  • 应该考虑在析构时自动 join 或 detach 线程

错误处理:可以增加更多错误检查和更详细的错误信息

线程取消

  • Stop() 使用 pthread_cancel,这可能导致资源泄漏

  • 更好的方式是提供协作式的取消机制

移动语义:可以添加移动构造函数和移动赋值运算符,避免不必要的拷贝

5、总结

这个线程封装类展示了如何将 POSIX 线程封装成 C++ 对象,关键点在于:

  • 使用静态成员函数作为线程入口点

  • 通过 void* 参数传递 this 指针

  • 在静态函数中转换回对象指针并调用成员方法

  • 使用 std::function 灵活地包装各种可调用对象

        这种设计模式在 C++ 中很常见,特别是在需要与 C 接口交互时(如 POSIX 线程、信号处理等)。理解 this 指针的传递机制是掌握这种封装技术的关键。


五、模板化 Thread 类的改动

#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; // bugtemplate<typename T>class Thread{using func_t = std::function<void(T)>; // 暂时这样写,完全够了private:void EnableDetach(){std::cout << "线程被分离了" << std::endl;_isdetach = true;}void EnableRunning(){_isrunning = true;}static void *Routine(void *args) // 属于类内的成员函数,默认包含this指针!{Thread<T> *self = static_cast<Thread<T> *>(args);self->EnableRunning();if (self->_isdetach)self->Detach();self->_func(self->_data); // 回调处理return nullptr;}// bugpublic:Thread(func_t func, T data): _tid(0),_isdetach(false),_isrunning(false),res(nullptr),_func(func),_data(data){_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 << "create 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;T _data;};
}#endif

将 Thread 类从非模板改为模板(template<typename T>)后,主要改动如下:

1、模板化的核心改动

(1) 模板参数 T 的引入

template<typename T>
class Thread {using func_t = std::function<void(T)>; // 回调函数类型,接受 T 类型参数
private:func_t _func;  // 存储回调函数T _data;       // 存储线程执行时传入的数据
};
  • 作用:允许线程执行时携带任意类型的数据(T),并通过 std::function<void(T)> 回调处理。

  • 示例用法

    void PrintInt(int x) { std::cout << x << std::endl; }
    ThreadModlue::Thread<int> t(PrintInt, 42); // 线程执行时传入 int 类型数据

(2) 回调函数和数据的存储

  • _func 现在是 std::function<void(T)>,接受 T 类型参数。

  • _data 是 T 类型,在线程启动时传递给 _func

2、关键逻辑改动

(1) Routine 静态成员函数

static void *Routine(void *args) {Thread<T> *self = static_cast<Thread<T> *>(args);self->EnableRunning();if (self->_isdetach)self->Detach();self->_func(self->_data); // 执行回调,传入 _datareturn nullptr;
}
  • 改动:线程入口函数现在会调用 _func(_data),将 _data 传递给回调函数。

  • 问题:如果 T 是复杂类型(如非平凡可拷贝对象),直接传递 _data 可能导致性能问题(建议改用 const T& 或 T&&)。

(2) 构造函数

Thread(func_t func, T data): _tid(0), _isdetach(false), _isrunning(false), res(nullptr),_func(func), _data(data) // 初始化 _data
{_name = "thread-" + std::to_string(number++);
}
  • 改动:新增 T data 参数,用于初始化 _data

  • 问题:如果 T 是大型对象(如 std::vector),拷贝构造 _data(data) 可能影响性能(建议用 std::move 或 const T&)。

http://www.dtcms.com/a/546183.html

相关文章:

  • Vue.js 循环语句
  • SourceForge 节点介绍
  • 合肥门户网站有哪些做推广一般那些网站比较好
  • 西安建网站网站推广番禺人才网局网
  • 银川建立网站企业网站注册
  • 2016响应式网站模版wordpress七牛图片插件
  • 藁城住房和城乡建设局网站wordpress 多用户商城主题
  • 入门级头戴游戏耳机推荐:罗技G321无线游戏耳机,像“优衣库”一样懂你的刚需
  • 秦皇岛做网站的公司哪家好互联网产品推广案例范文
  • wordpress建站心得namesilo wordpress
  • 织梦怎么修改网站标题那些网站可以做自媒体
  • Vue3 Composables 全面使用指南 - 现代化逻辑复用方案
  • 有模板怎么做网站同城信息商家的网站开发
  • 量子处理器与 GPU 超级计算机互联
  • 网站建设的美图wordpress用户注册插件汉化
  • 网站建设价格方案龙华网站建设哪家公司好
  • 建设医院网站ppt模板photoshop手机版免费
  • 物联网设备固件版本智能管理与自动化更新策略
  • 大模型-qwen+audio的vllm部署初探-1
  • 翠峦网站建设网站做浮动边框asp代码
  • 网站建设需求分析表怎么写seo公司排行
  • corepack enable是什么 有什么作用
  • 免费网站建设培训班网站建设过程有哪几个阶段
  • leetcode 227 基本计算器II
  • 酒泉网站建设与制作网站结构有哪些
  • 【Vue】若依框架树形选择器和显示
  • 力扣hot100之最长连续序列(java版)
  • 买了两台服务器可以做网站吗seoapp推广
  • GXDE For deepin 25:deepin25 能用上 GXDE 了!
  • 搜房网站要怎么 做网站做成app