基于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标志)。
资源管理缺失:如果线程未分离也未 Join,Thread 对象的析构函数(当前为空)可能导致资源泄漏。
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&)。
