初识Linux · 线程控制(1)

发布时间 - 2025-06-20 00:00:00    点击率:

前言:

在前一篇文章中,我们已经探讨了线程的基本概念,了解到可以通过ps -aL命令查看线程。由于线程的特殊性,我们需要在用户层和操作系统层之间添加一个线程库,并在编译程序时将程序链接到这个线程库。

我们也初步学习了如何创建线程,使用pthread_create函数。然而,仅通过创建和概念来学习线程是远远不够的。因此,本文将详细介绍线程的各种操作。

我们将从多个角度探讨问题,然后尝试模拟实现一个线程。


我们将从多个子问题出发来全面了解线程操作。

子问题一:主线程和新线程之间谁先运行?让我们看一段代码:

void* threadRun(void* args){
    std::string name = static_cast(args);
    std::cout << name << " is running" << std::endl;
    return nullptr;
}

int main(){ pthread_t tid; pthread_create(&tid, nullptr, threadRun, (void)"thread -1"); std::cout << "main thread is running" << std::endl; return 0; }

我们期望看到屏幕上打印两条语句,以便讨论谁先运行的问题。然而,实际运行多次后,却没有看到子线程的结果。

这是因为我们没有调用join函数。主线程在创建子线程后,不会等待子线程完成任务就直接退出。如果运气好,子线程比主线程先完成任务,我们就能看到子线程的输出。

通过这个问题,我们可以清楚地看到,主线程和子线程的运行顺序是随机的,这与进程的父子进程调度顺序类似。

子问题二:我们期望谁是最后退出的呢?为什么?

int main(){
pthread_t tid;
void result;
pthread_create(&tid, nullptr, threadRun, (void)"thread -1");
std::cout << "main thread is running" << std::endl;
pthread_join(tid, &result);
return 0;
}

对于这个问题,基于进程的基础知识,我们可以推测期望主线程最后退出。就像父进程需要收集子进程的退出信息一样,子线程退出时,其信息也需要传递给主线程。我们如何查看子线程的信息呢?

在前一篇文章中,我们提到pthread_create的第一个参数是输出参数,第二个参数是线程属性,我们暂时将其设为nullptr。第三个参数是函数指针,返回值和参数都是void。我们可以直接使用这个参数,因为它来自pthread_create的第四个参数。

当我们以16进制打印tid时,会发现它是一个地址。如果主线程不等待子线程,可能会出现类似于僵尸进程的问题。因此,我们期望主线程是最后退出的线程。

子问题三:tid是什么?

std::string ToHex(pthread_t& tid){
char buffer[128];
snprintf(buffer, sizeof(buffer), "0x%lx", tid);
return buffer;
}

通过查看pthread_t的源码,我们发现它实际上是一个无符号的长整型。我们通过该函数将其以16进制形式打印出来:

它是一个地址,理解到这里就足够了,关于它的更深层次内容,我们将在后续讨论。

子问题四:全面看待线程函数的传参,我们到底能传些什么?

class ThreadData{
public:
std::string _name;
int _id;
};

void threadRunClass(void args){ ThreadData t = (ThreadData)args; std::cout << t._name << " " << t._id << " is running" << std::endl; return nullptr; }

int main(){ pthread_t tid1; ThreadData t1; t1._name = "thread -1"; t1._id = 1; pthread_create(&tid1, nullptr, threadRunClass, (void)&t1); pthread_join(tid1, nullptr); return 0; }

对于最后一个参数,我们可以传递字符串,那么我们能传递自定义的类成员吗?

当然可以。如果你问为什么,我只能说:当然可以啊。

所以我们可以实现这样的效果:

然而,如果我们在主线程的栈区修改t1,可能会导致问题。因为线程有自己的栈,如果我们传递的是局部变量的地址,所有线程都可以看到这个地址,如果其中一个线程修改了它,就会引起问题。

因此,最安全的方式是在堆上分配空间,然后传递指针。

int main(){
pthread_t tid1;
ThreadData t1;
t1._name = "thread -1";
t1._id = 1;
ThreadData td = new ThreadData();
td->_name = "thread-2";
pthread_create(&tid1, nullptr, threadRunClass, (void*)&t1);
pthread_create(&tid1, nullptr, threadRunClass, td);
pthread_join(tid1, nullptr);
return 0;
}

对于这个问题,我们不能局限于传递内置类型,我们可以传递任何类型。

子问题五:全面看待线程的返回值。对于线程返回值的问题,类似于线程函数的传参,我们可以返回不仅仅是nullptr,而是任意类型,只需进行强制类型转换即可。

同学,到现在,你理解C语言或C++的泛型指针了吗?

class ThreadResult{
public:
bool _result;
};

class ThreadData{ public: std::string _name; };

void threadRun(void args){ ThreadData t = static_cast>(args); std::cout << t->_name << " is running" << std::endl; ThreadResult s = new ThreadResult(); s->_result = true; return (void)s; }

int main(){ pthread_t tid; ThreadResult result = nullptr; ThreadData t = new ThreadData(); t->_name = "thread -1"; pthread_create(&tid, nullptr, threadRun, (void*)t); pthread_join(tid, (void**)&result); std::cout << "Thread result: " << result->_result << std::endl; delete t; delete result; return 0; }

这段代码是否还需要进一步解释呢?


# linux  # c语言  # 操作系统  # ai  # c++  # 为什么  # 整型  # 局部变量  # 字符串  # 强制类型转换  # void  # 指针  #   #   # 输出参数  # 泛型  # 线程  # 主线程  # 类型转换  # 我们可以  # 这个问题  # 多个  # 返回值  # 将其  # 它是  # 在前  # 类似于  # 完成任务  # 一篇文章 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: Internet Explorer官网直接进入 IE浏览器在线体验版网址  如何自定义建站之星模板颜色并下载新样式?  Laravel如何使用集合(Collections)进行数据处理_Laravel Collection常用方法与技巧  如何在IIS中新建站点并解决端口绑定冲突?  javascript和jQuery中的AJAX技术详解【包含AJAX各种跨域技术】  奇安信“盘古石”团队突破 iOS 26.1 提权  Laravel怎么为数据库表字段添加索引以优化查询  为什么要用作用域操作符_php中访问类常量与静态属性的优势【解答】  微信小程序制作网站有哪些,微信小程序需要做网站吗?  JS实现鼠标移上去显示图片或微信二维码  Laravel如何使用Facades(门面)及其工作原理_Laravel门面模式与底层机制  如何将凡科建站内容保存为本地文件?  Angular 表单中正确绑定输入值以确保提交与验证正常工作  iOS中将个别页面强制横屏其他页面竖屏  IOS倒计时设置UIButton标题title的抖动问题  Python文本处理实践_日志清洗解析【指导】  标题:Vue + Vuex 项目中正确使用 JWT 进行身份认证的实践指南  Win10如何卸载预装Edge扩展_Win10卸载Edge扩展教程【方法】  中山网站制作网页,中山新生登记系统登记流程?  Laravel怎么调用外部API_Laravel Http Client客户端使用  Edge浏览器怎么启用睡眠标签页_节省电脑内存占用优化技巧  Laravel控制器是什么_Laravel MVC架构中Controller的作用与实践  Laravel如何使用Eloquent ORM进行数据库操作?(CRUD示例)  专业商城网站制作公司有哪些,pi商城官网是哪个?  瓜子二手车官方网站在线入口 瓜子二手车网页版官网通道入口  如何利用DOS批处理实现定时关机操作详解  javascript中的try catch异常捕获机制用法分析  PHP正则匹配日期和时间(时间戳转换)的实例代码  如何快速登录WAP自助建站平台?  Python文件流缓冲机制_IO性能解析【教程】  Win11摄像头无法使用怎么办_Win11相机隐私权限开启教程【详解】  Laravel如何记录日志_Laravel Logging系统配置与自定义日志通道  jimdo怎样用html5做选项卡_jimdo选项卡html5实现与切换效果【指南】  php静态变量怎么调试_php静态变量作用域调试技巧【解答】  如何在万网自助建站中设置域名及备案?  如何有效防御Web建站篡改攻击?  文字头像制作网站推荐软件,醒图能自动配文字吗?  canvas 画布在主流浏览器中的尺寸限制详细介绍  laravel怎么配置Redis作为缓存驱动_laravel Redis缓存配置教程  如何在云服务器上快速搭建个人网站?  如何基于云服务器快速搭建网站及云盘系统?  如何在IIS7中新建站点?详细步骤解析  如何在Ubuntu系统下快速搭建WordPress个人网站?  哪家制作企业网站好,开办像阿里巴巴那样的网络公司和网站要怎么做?  在线ppt制作网站有哪些软件,如何把网页的内容做成ppt?  如何用搬瓦工VPS快速搭建个人网站?  laravel怎么通过契约(Contracts)编程_laravel契约(Contracts)编程方法  如何在 Pandas 中基于一列条件计算另一列的分组均值  php嵌入式断网后怎么恢复_php检测网络重连并恢复硬件控制【操作】  微博html5版本怎么弄发语音微博_语音录制入口及时长限制操作【教程】