初识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版本怎么弄发语音微博_语音录制入口及时长限制操作【教程】
下一篇:超级教练和驾考宝典哪个好用
下一篇:超级教练和驾考宝典哪个好用

