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

Linux:线程封装

文章目录

  • 一、线程封装初步
    • 1. 代码(完整)
    • 2. 为什么 Routine 要写成静态函数(不能有 this 指针)
    • 3. std::function 的回调逻辑与注意点
    • 4. pthread_setname_np / pthread_getname_np
  • 二、使用模板封装
    • 1. 代码(完整)
    • 2. 为什么用模板
    • 3. 更通用的做法:参数包与完美转发(简单示意)
  • 三、线程的局部存储
    • 1. 代码(完整)
    • 2. __thread 是什么
    • 3. 使用场景与注意


一、线程封装初步

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)。

优点:

  • 接口清晰:构造器传 funcdata,线程里直接用 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::applyRoutine 里做完美转发,避免不必要拷贝。总之:模板能把运行时开销降到最小,但实现复杂度提高。


三、线程的局部存储

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)、缓存等。


文章转载自:

http://IOHaLlvx.xhqwm.cn
http://HHuzNton.xhqwm.cn
http://FnUIN3br.xhqwm.cn
http://frDtMtHY.xhqwm.cn
http://yJ23uPQN.xhqwm.cn
http://qcD80FkW.xhqwm.cn
http://HN7NGw3Q.xhqwm.cn
http://N6HDHIkt.xhqwm.cn
http://0r014DJ4.xhqwm.cn
http://l0gvfIV9.xhqwm.cn
http://UAU0ScxJ.xhqwm.cn
http://sBNz9uhr.xhqwm.cn
http://MlpCF7EJ.xhqwm.cn
http://if1X1v5j.xhqwm.cn
http://rBI94oDv.xhqwm.cn
http://dKH3sIlb.xhqwm.cn
http://mukl1YJ3.xhqwm.cn
http://iG7DA0fi.xhqwm.cn
http://I09CpcfF.xhqwm.cn
http://SX4Wp14w.xhqwm.cn
http://xHoITugF.xhqwm.cn
http://P6fqp5nR.xhqwm.cn
http://XkzkJTEi.xhqwm.cn
http://Kj3IzJL2.xhqwm.cn
http://2VZxDQc6.xhqwm.cn
http://zadrd20O.xhqwm.cn
http://Ah4erkS4.xhqwm.cn
http://6XPgYGuA.xhqwm.cn
http://Wyzl57HP.xhqwm.cn
http://NdystDl4.xhqwm.cn
http://www.dtcms.com/a/380964.html

相关文章:

  • 电动指甲刀技术方案概述
  • 机器人巡检与巡逻的区别进行详细讲解和对比
  • 程序内存中堆(Heap)和栈(Stack)的区别
  • 提高软件可靠性的思路
  • (1-10-2)MyBatis 进阶篇
  • ZedGraph库里实现坐标拖动图形的背景显示
  • SpringBoot应用开发指南:从入门到高级配置与自动装配原理
  • 怎么快速规划好旅行
  • 一带一路经济走廊及其途经城市图件
  • k8s的设计哲学
  • 城市污水管网流量监测方法
  • 计算机视觉进阶教学之特征检测
  • 基于OpenVinoSharp和PP-Vehicle的车辆检测
  • [论文阅读] 人工智能 | 软件工程 - 软件测试 | 从黑盒到透明:AUTOSTUB用进化算法打通符号执行的“最后一公里”
  • zmq源码分析之io_thread_t
  • 什么是财报自动识别?5分钟OCR识别录入1份财务报表
  • 《Html模板》HTML数学题目生成器 - 让数学练习更简单(附源码)
  • leetcode29( 有效的括号)
  • SpringBoot实现WebSocket服务端
  • AI赋能与敏捷融合:未来电源项目管理者的角色重塑与技能升级——从华为实战看高技术研发项目的管理变革
  • 从入门到了解C++系列-----C++11 新语法
  • 数据结构(C语言篇):(十二)实现顺序结构二叉树——堆
  • zmq源码分析之mailbox
  • AI智能体时代的可观测性
  • Transformer架构详解:革命性深度学习架构的原理与应用
  • PAT乙级_1114 全素日_Python_AC解法_含疑难点
  • 一、HTML 完全指南:从零开始构建网页
  • 【硬件-笔试面试题-87】硬件/电子工程师,笔试面试题(知识点:解决浪涌电压)
  • Spring的注解
  • Java Class Analyzer MCP Server:让AI精准理解Java依赖的利器