尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Linux之线程(四)----简单封装一个线程
整体大框架#pragma once #include iostream #include pthread.h #include string namespace ThreadModul { static int gnumber1; enum class TSTATUS { THREAD_NEW, THREAD_RUNNING, THREAD_STOP }; class Thread { public: Thread() :_tid(-1) ,_status(TSTATUS::THREAD_NEW) { _namethreadto_string(gnumber); } ~Thread() { } void Start() { } void Stop() { } void Detach() { } void Join() { } private: string _name; pthread_t _tid; TSTATUS _status; bool _joinable; }; } // namespThreadModul构造将线程基本的属性设置_tid----1_name-------用thread加上数字状态-----TSTATUS----THREAD__NEW将线程是否可以进行被等待默认设置为1创建新线程这里需要有让线程执行的函数我们发现这里会报错???pthread_create要求传入的线程入口函数必须是这种原型void *(*)(void *)也就是普通 C 风格函数指针没有隐含 this 参数C 里非 static 成员函数会藏一个隐形参数this真实原型等价于void *ThreadRoutine(Thread* this, void* args);函数签名变成void *(*)(Thread*, void *)和 pthread 要求的void *(*)(void *)不匹配→ 类型报错如何解决呢???就是在前面加上static当我们调用start的时候线程就开始运行了所以与其相关的状态就要发生改变变成THREAD_RUNNING直接在函数里面修改的话,我们发现不行,因为这个函数以及没有this指针那么我们应该要如何来解决???Start()调用pthread_create时把当前对象的this作为第四个参数传进去pthread_create(_tid, nullptr, ThreadRoutine, this);在 static 的ThreadRoutine里面把args还原成Thread*对象指针通过这个self指针调用成员方法ToRunning()间接修改成员_statusstatic 线程例程本身没有 this 指针无法直接操作对象成员 解决方案创建线程时传入对象this在线程入口里还原出对象指针再通过指针调用成员函数访问成员变量。到现在为止,我们难道就是让线程来改变自己的状态吗,显然不是的,我们还需要进一步让线程完成任务,像c那样将任务传入,让线程自己去完成任务,添加成员变量初始化成员变量让新线程执行相关任务新线程运行完要将状态变成THREAD_STOP测试一下更改线程的名字pthread_setname_np/pthread_getname_npnp non-portable非 POSIX 标准Linux glibc 独有别的系统macOS/BSD没有这个接口这是重点用来给 Linux 内核层面的线程设置 / 读取线程名称方便ps、htop、top等命令直接看名字调试属于调试工具类 API不影响业务逻辑。 pthread_setname_np(thread, name)给指定 tid 的线程设置内核线程名 pthread_getname_np(thread, buf, size)读取指定 tid 线程在内核里存的名字分离线程调用pthread_detach线程的状态时running,并且要求_jionable是true等待线程调用pthread_join要求_jionable是true线程终止调用pthread_cancel测试为了方便观察我们加一点函数打印一些值正常结束void func() { char buf[64]{0}; pthread_getname_np(pthread_self(), buf, sizeof(buf)); string name buf; int cnt5; while(cnt--) { coutnew thread running:nameendl; sleep(1); } } int main() { ThreadModul::Thread t(func); t.Start(); sleep(10); t.PrintInfo(); t.Join(); t.PrintInfo(); while (1) { cout主线程还在运行endl; sleep(1); } return 0; }主线程主动将新线程杀死void func() { char buf[64]{0}; pthread_getname_np(pthread_self(), buf, sizeof(buf)); string name buf; while(1) { coutnew thread running:nameendl; sleep(1); } } int main() { ThreadModul::Thread t(func); t.Start(); sleep(5); t.PrintInfo(); //t.Detach(); t.PrintInfo(); t.Stop(); t.PrintInfo(); t.Join(); t.PrintInfo(); while (1) { cout主线程还在运行endl; sleep(1); } return 0; }线程分离void func() { char buf[64]{0}; pthread_getname_np(pthread_self(), buf, sizeof(buf)); string name buf; int cnt5; while(cnt--) { coutnew thread running:nameendl; sleep(1); } } int main() { ThreadModul::Thread t(func); t.Start(); t.PrintInfo(); t.Detach(); t.PrintInfo(); sleep(5); t.Stop(); t.PrintInfo(); t.Join(); t.PrintInfo(); while (1) { cout主线程还在运行endl; sleep(1); } return 0; }以上都是创建一个新线程但是实际上是有很多的新线程我们要对线程进行管理我们可以用queue来进行管理,在添加一些让线程暂停,过段时间再让现场重新入队,这样我们就实现了一个调度器管理接下来我们用vector来对其进行管理#include iostream #include Thread.hpp #include unistd.h #include string #include vector using namespace std; void func() { char buf[64] {0}; pthread_getname_np(pthread_self(), buf, sizeof(buf)); string name buf; int cnt 5; while (1) { cout new thread running: name cnt: cnt endl; sleep(1); } } int main() { vectorThreadModul::Thread threads; for (int i 0; i 10; i) { ThreadModul::Thread t(func); threads.emplace_back(t); } for (auto e : threads) { e.Start(); } sleep(5); for (auto e : threads) { e.Stop(); } for (auto e : threads) { e.Join(); e.PrintInfo(); } while (1) { cout 主线程还在运行 endl; sleep(1); } return 0; }线程函数传参问题可以用可变参数可以用模板来解决接下来我们用模板来实现#pragma once #include iostream #include pthread.h #include string #include functional using namespace std; typedef std::functionvoid() callback; namespace ThreadModul { templatetypename T using callbackfunctionvoid(T); static int gnumber 1; enum class TSTATUS { THREAD_NEW, THREAD_RUNNING, THREAD_STOP }; templatetypename T class Thread { private: string GetStatus() { if (_status TSTATUS::THREAD_NEW) return THREAD_NEW; else if (_status TSTATUS::THREAD_RUNNING) return THREAD_RUNNING; else return THREAD_STOP; } void ToRunning() { _status TSTATUS::THREAD_RUNNING; } void ToStopping() { _status TSTATUS::THREAD_STOP; } void Setname() { pthread_setname_np(_tid, _name.c_str()); } string GetName() { char buf[64] {0}; pthread_getname_np(_tid, buf, sizeof(buf)); return buf; } static void *ThreadRoutine(void *args) { ThreadT *self static_castThreadT *(args); self-ToRunning(); self-Setname(); self-_cb(self-_data); self-ToStopping(); return nullptr; } public: Thread(callbackT cb,const T data) : _tid(-1), _status(TSTATUS::THREAD_NEW), _joinable(true), _cb(cb) ,_data(data) { _name NEW-thread- to_string(gnumber); } ~Thread() { } bool Start() { // 创建新线程 int n pthread_create(_tid, nullptr, ThreadRoutine, this); if (n ! 0) return false; else { ToRunning(); return true; } } void Stop() { if (_status TSTATUS::THREAD_RUNNING) { int n pthread_cancel(_tid); if (n 0) { _statusTSTATUS::THREAD_STOP; cout 线程已经跑完 endl; } else { perror(pthread_cancel); } } else { cerr 该线程终止失败 endl; } } void Detach() { if (_joinable true _status TSTATUS::THREAD_RUNNING) { _joinable false; int n pthread_detach(_tid); if (n 0) { cout 该线程和主线程分离成功 endl; } else { perror(pthread_detach); _joinable true; } } else { cerr 该线程不可分离 endl; } } void Join() { if (_joinable true ) { int n pthread_join(_tid, _retval); if (n 0) { cout 主线程等待该线程 endl; } else { perror(pthread_join); } } else { cerr 该线程不可被等待 endl; } } void PrintInfo() { cout 线程name: _name endl; cout 线程tid: _tid endl; cout 线程status: GetStatus() endl; cout 线程joinable: _joinable endl; coutendlendl; } private: string _name; pthread_t _tid; TSTATUS _status; bool _joinable; callbackT _cb; T _data; void *_retval; }; } // namespThreadModul#include iostream #include Thread.hpp #include unistd.h #include string #include vector using namespace std; void func(int cnt) { char buf[64] {0}; pthread_getname_np(pthread_self(), buf, sizeof(buf)); string name buf; while (cnt) { cout new thread running: name cnt: cnt-- endl; sleep(1); } // while (cnt--) // { // cout new thread running: name cnt:cnt endl; // sleep(1); // } } int main() { vectorThreadModul::Threadint threads; for (int i 0; i 10; i) { threads.emplace_back(func ,10); } for (auto e : threads) { e.Start(); } sleep(5); // for (auto e : threads) // { // e.Stop(); // } for (auto e : threads) { e.Join(); e.PrintInfo(); } // ThreadModul::Thread t(func); // t.Start(); // t.PrintInfo(); // t.Detach(); // t.PrintInfo(); // sleep(5); // t.Stop(); // t.PrintInfo(); // t.Join(); // t.PrintInfo(); while (1) { cout 主线程还在运行 endl; sleep(1); } return 0; }上面实现了传一个参数的解法,但是我们如果要传入多个参数应该怎么办呢???这个时候就要用类来解决class Task { public: Task(int x, int y) : _x(x), _y(y) { } void Excute() { _ret _x _y; } string PrintAns() { return to_string(_x) to_string(_y) to_string(_ret); } private: int _x; int _y; int _ret; }; int main() { srand(time(nullptr) ^ getpid()); vectorTask tasks; for (int i 0; i 10; i) { tasks.emplace_back(rand()%101,3); } vectorThreadModul::Thread threads; for (int i 0; i 10; i) { threads.emplace_back([i,tasks](){ tasks[i].Excute(); }); } for (auto e : threads) { e.Start(); } sleep(5); for (auto e : threads) { e.Join(); e.PrintInfo(); } for (auto e : tasks) { coute.PrintAns()endl; } while (1) { cout 主线程还在运行 endl; sleep(1); } return 0; }解耦「任务内容」和「线程执行载体」构造 10 个加法 Task构造 10 个线程对象绑定各自任务回调批量 Start创建内核线程并行执行 Excute 加法瞬间完成子线程直接结束主线程 sleep5s主线程依次 Join 等待每个子线程回收资源打印所有加法结果主线程死循环常驻Task tk(10,20);在主线程栈上创建任务对象_x10_y20ThreadModul::Thread t( [tk]()-void { tk.Excute(); } );构造线程对象传入 Lambda 回调[tk]引用捕获 tklambda 内部直接操作外面同一个 Task 对象不拷贝回调逻辑调用Excute()计算1020把结果写入 tk 内部的_rett.Start();启动子线程子线程执行 lambdat.Join();主线程阻塞等待子线程完全跑完才往下走关键保障couttk.PrintAns()打印102030Lambda 是什么Lambda 是 C11 引入的匿名函数可以在代码局部就地定义一个临时函数不需要写全局函数、不需要写仿函数非常适合线程回调sort 排序自定义规则STL 算法回调异步任务提交核心价值就地定义、就地使用、简洁、适配 std::function[捕获列表](参数) mutable - 返回值 { 函数体 }捕获方法[x] // 值捕获拷贝一份只读 [x] // 引用捕获用原变量同步修改 [] // 默认全部值捕获 [] // 默认全部引用捕获
RELATED

相关推荐

轻量级CNN在结膜图像贫血检测中的工程实践与部署指南

轻量级CNN在结膜图像贫血检测中的工程实践与部署指南

作为一名长期关注医疗AI落地的开发者,我注意到一个现象:很多前沿的医学影像AI研究,代码和模型往往“藏在深闺”,普通开发者或学生想复现、学习,门槛极高。今天要讨论的“基于轻量级卷积神经网络的结膜图像贫血检测”就…

📅 2026/10/17 10:46:34
风在吼,马在叫:当用户画像被蒸馏,Harness之火还能火多久?

风在吼,马在叫:当用户画像被蒸馏,Harness之火还能火多久?

风在吼,马在叫:当用户画像被蒸馏,Harness之火还能烧多久? ——从Harness的本质到Token分红,一个可复现的跨会话记忆现象调查 摘要 2026年8月,作者在一次跨会话对话中发现:窗口A中凝练的独特隐喻…

📅 2026/9/5 0:15:53
B站缓存转MP4一步到位:m4s-converter无损合并,1分钟免费告别“文件打不开“

B站缓存转MP4一步到位:m4s-converter无损合并,1分钟免费告别“文件打不开“

B站缓存转MP4一步到位:m4s-converter无损合并,1分钟免费告别"文件打不开" 【免费下载链接】m4s-converter 一个跨平台小工具,将bilibili缓存的m4s格式音视频文件合并成mp4 项目地址: https://gitcode.com/gh_mirrors/m4/m4s-conv…

📅 2026/10/13 18:41:47
MORE NEWS

更多资讯

📰

低成本长视频理解新思路:智能导航先找关键片段

这两年做长视频理解的同行,应该都有同一个感受:短视频赛道的模型已经卷到天上,但一碰到长视频,大家又集体回到原始社会。要么把视频抽帧抽到地老天荒,要么把整段视频暴力塞进上下文,显卡当场冒烟。我自己做…

📰

Java调用Kubernetes创建Pod的底层原理与生产实践

1. 这不是“调个API”那么简单:Java调用K8s创建Pod的本质是什么“Java调用K8s创建Pod”这九个字,表面看是两套技术栈的简单对接——后端语言连上容器编排平台。但干过三年以上K8s落地项目的人都清楚,这背后根本不是写几行RestTemplate就能搞定…

📰

RK3588 Android 12设备权限修改:从chmod到SELinux

1. 场景先行:你遇到的"设备权限问题"到底属于哪一类经常有做 RK3588 项目的朋友问我类似的问题:"我在 Android 12 上访问 /dev/ttyS4 报 permission denied,设备权限怎么改?" 说实话,这个问题没法…

📰

语音识别入门实战:从原理到搭建可运行的转写系统

语音识别(ASR)是这几年普及速度最快的 AI 技术之一。手机输入法、会议转写、客服质检、字幕生成,背后都离不开同一套"把声音变成文字"的链路。对新手来说,最大的障碍不是原理复杂,而是不知道从哪儿下手——网…

📰

可解释性AI落地指南:LIME与SHAP工具选型及避坑实践

如果你在业务里待过,一定见过这种场景:模型上线前,业务方的第一反应不是问你 AUC 涨了多少,而是问“它凭什么给这个客户打了低分”。你说“这是 GBST 模型的输出”,对方只会皱着眉摇头。这就是可解释性人工智能要解决的…

📰

欧拉公式:数学中最优雅的常数统一之美

1. 从一条“最美公式”说起:欧拉公式到底是什么我在刚开始接触高等数学的时候,就被一条公式深深震撼过。它不是那种需要背很多前提才能记住的复杂结论,恰恰相反,它简洁到可以用一行写完。这条公式就是欧拉公式:e^ix c…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬