文章目录
一、线程封装初步
1. 代码(完整)
#include "Thread.hpp"
#include <vector>
#include <unistd.h>
using namespace ThreadModule;
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;
sleep(1);
return nullptr;
} });
}
for (auto &t : threads)
{
t.Start();
}
for (auto &t : threads)
{
t.Join();
}
return 0;
}
#ifndef _THREAD_H_
#define _THREAD_H_
#include <pthread.h>
#include <iostream>
#include <string>
#include <functional>
#include <stdint.h>
#include <string.h>
namespace ThreadModule
{
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++);
}
~Thread() {}
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 << "pthread_create error: " << strerror(n) << std::endl;
return false;
}
else
{
std::cout << "Thread " << _name << " started." << std::endl;
return true;
}
}
bool Stop()
{
if (_isrunning)
{
int n = pthread_cancel(_tid);
if (n != 0)
{
std::cerr << "pthread_cancel error: " << strerror(n) << std::endl;
return false;
}
else
{
_isrunning = true;
std::cout << "Thread " << _name << " stopped." << 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;
}
}
private:
pthread_t _tid;
std::string _name;
bool _isdetach;
bool _isrunning;
void *res;
func_t _func;
};
}
#endif
2. 为什么 Routine 要写成静态函数(不能有 this 指针)
pthread_create
要求回调类型是 void*(*)(void*)
(C 风格函数指针)。类的非静态成员函数有隐含的 this
参数,签名不匹配,因此不能直接作为 pthread_create
的入口。常见而正确的做法是:
- 将入口写成
static
或全局函数; - 把
this
(或对象指针)作为pthread_create
的第四个参数传进去; - 在
Routine
里把void*
转回Thread*
,再调用成员方法和成员数据。
3. std::function 的回调逻辑与注意点
使用 std::function<void()>
的优点:
- 接口简单,支持 lambda、function、bind 等各种可调用对象;
- 上层只需给出可执行任务,线程类负责生命周期管理。
4. pthread_setname_np / pthread_getname_np
可以把线程名字设置进去,然后通过get再获取回来
二、使用模板封装
1. 代码(完整)
#include "Thread.hpp"
#include <unistd.h>
using namespace ThreadModule;
class ThreadData
{
public:
pthread_t tid;
std::string name;
};
void Count(ThreadData td)
{
int a = 5;
while (a--)
{
std::cout << "我是一个新线程" << std::endl;
sleep(1);
}
}
int main()
{
ThreadData td;
Thread<ThreadData> t(Count, td);
t.Start();
t.Join();
t.Start();
t.Detach();
sleep(5);
t.Stop();
sleep(5);
t.Join();
return 0;
}
#ifndef _THREAD_H_
#define _THREAD_H_
#include <pthread.h>
#include <iostream>
#include <string>
#include <functional>
#include <stdint.h>
#include <string.h>
namespace ThreadModule
{
static uint32_t number = 1;
template<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)
{
Thread<T>* self = static_cast<Thread<T>*>(args);
self->EnableRunning();
if (self->_isdetach)
self->Detach();
pthread_setname_np(self->_tid, self->_name.c_str());
self->_func(self->_data);
return nullptr;
}
public:
Thread(func_t func, T data)
: _tid(0), _isdetach(false), _isrunning(false), res(nullptr), _func(func), _data(data)
{
_name = "Thread" + std::to_string(number++);
}
~Thread() {}
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 << "pthread_create error: " << strerror(n) << std::endl;
return false;
}
else
{
std::cout << "Thread " << _name << " started." << std::endl;
return true;
}
}
bool Stop()
{
if (_isrunning)
{
int n = pthread_cancel(_tid);
if (n != 0)
{
std::cerr << "pthread_cancel error: " << strerror(n) << std::endl;
return false;
}
else
{
_isrunning = true;
std::cout << "Thread " << _name << " stopped." << 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;
}
}
private:
pthread_t _tid;
std::string _name;
bool _isdetach;
bool _isrunning;
void *res;
func_t _func;
T _data;
};
}
#endif
2. 为什么用模板
模板化的 Thread<T>
允许你把“要传入线程的参数”封装成任意类型 T
,再在 Routine
调用时把该 T
传给用户提供的回调函数。这种方式适合参数结构固定、希望一次性把参数打包传入的场景(例如 ThreadData
)。
优点:
- 接口清晰:构造器传
func
和data
,线程里直接用data
; - 对于小结构体或 POD 非常方便。
3. 更通用的做法:参数包与完美转发(简单示意)
如果想让线程接受任意数量和类型的参数,可用可变参数模板 + 完美转发。示意(简洁版):
template<typename Func, typename... Args>
class ThreadVar {
public:
ThreadVar(Func&& f, Args&&... args)
: _task(std::bind(std::forward<Func>(f), std::forward<Args>(args)...)) {}
bool Start() {
pthread_create(&_tid, nullptr, &ThreadVar::Routine, this);
}
private:
static void* Routine(void* arg) {
auto* self = static_cast<ThreadVar*>(arg);
self->_task(); // 执行绑定后的闭包
return nullptr;
}
std::function<void()> _task;
};
或者更高级用 std::tuple
+ std::apply
在 Routine
里做完美转发,避免不必要拷贝。总之:模板能把运行时开销降到最小,但实现复杂度提高。
三、线程的局部存储
1. 代码(完整)
#include <pthread.h>
#include <iostream>
#include <string>
#include <unistd.h>
// 该count叫做线程的局部存储!
__thread int count = 1;
// 线程局部存储有什么用?全局变量,我又不想让这个全局变量被其他线程看到!
// 线程局部存储,只能存储内置类型和部分指针
std::string Addr(int &c)
{
char addr[64];
snprintf(addr, sizeof(addr), "%p", &c);
return addr;
}
void *routine1(void *args)
{
(void)args;
while (true)
{
std::cout << "thread - 1, count = " << count << "[我来修改count], "
<< "&count: " << Addr(count) << std::endl;
count++;
sleep(1);
}
}
void *routine2(void *args)
{
(void)args;
while (true)
{
std::cout << "thread - 2, count = " << count
<< ", &count: " << Addr(count) << std::endl;
sleep(1);
}
}
int main()
{
pthread_t tid1, tid2;
pthread_create(&tid1, nullptr, routine1, nullptr);
pthread_create(&tid2, nullptr, routine2, nullptr);
pthread_join(tid1, nullptr);
pthread_join(tid2, nullptr);
return 0;
}
2. __thread 是什么
__thread
声明的变量在每个线程有一份独立的实例 — 换句话说,每个线程看到的是自己那份变量,其他线程看不到你这份的值(也就是“__thread只有自己才能看见”)。这正是线程局部存储(TLS,Thread Local Storage)的含义。
在你的示例中,count
在两个线程里各自有独立地址和值,输出会显示不同的 &count
,和值互不干扰 —— 非常直观地证明了 TLS 的行为。🧠
3. 使用场景与注意
- 适用于线程本地计数器、日志上下文(thread id、trace id)、缓存等。