Linux多线程之线程控制

(。・∀・)ノ゙嗨!你好这里是ky233的主页:这里是ky233的主页,欢迎光临~icon-default.png?t=N7T8https://blog.csdn.net/ky233?type=blog

点个关注不迷路⌯'▾'⌯

目录

一、pthread_crate

二、pthread_join

三、pthread_exit和pthread_cancel

四、关于线程id的探索

五、pthread_self

六、线程的局部存储

七、线程调用execl

八、分离线程


一、pthread_crate

大部分上篇文章已经详细说过了,这里仅做补充

在多进程中,我们不知道父子进程谁先运行,同样的,多线程也是随机的

线程异常的情况下

#include <iostream>
#include <pthread.h>
#include <unistd.h>using namespace std;void *threadRoutine(void *args)
{while (1){cout << "新线程:" << (char *)args << "running..." << endl;sleep(1);int a = 100;a/=0;}
}int main()
{pthread_t tid;pthread_create(&tid, nullptr, threadRoutine, (void *)"thread 1");while (1){cout << "main线程:"<< "running..." << endl;sleep(1);}
}

这个时候我们就会发现,程序异常了,出现了8号信号,所以我们可以得出结论

线程一旦异常,都可能导致进程整体退出。

二、pthread_join

线程在创建并执行的时候,线程也是要进行等待的!如果主线程不等待,即会引起类似于进程的僵尸问题,导致内存泄漏!

int pthread_join(pthread_t thread,void **retval)
  • 参数一:线程id
  • 参数二:输出型参数,用于获取次线程的退出结果,如果不关心,可以传递 nullptrr
  • 返回值:失败返回错误码,成功为0

在调用了之后,我们的主线程会默认的阻塞,并等待新线程退出

#include <iostream>
#include <pthread.h>
#include <unistd.h>using namespace std;void *threadRoutine(void *args)
{int i = 0;while (1){cout << "新线程:" << (char *)args << "running..." << endl;sleep(1);if (i++ == 10)break;}cout<<"新线程退出"<<endl;return nullptr;
}
int main()
{pthread_t tid;pthread_create(&tid, nullptr, threadRoutine, (void *)"thread 1");pthread_join(tid, nullptr);cout << "主线程正在等待...主线程成功退出" << endl;
}

在新线程退出的时候,我们可以返回特定的值,但是需要强转一下,如(void*)10,那么这是返回给谁呢?答案是谁等你就给谁,一般是给主线程的,所以我们可以这样写来获取新线程的返回值

void *threadRoutine(void *args)
{int i = 0;while (1){cout << "新线程:" << (char *)args << "running..." << endl;// sleep(1);if (i++ == 10)break;}cout << "新线程退出" << endl;return (void *)10;
}
int main()
{pthread_t tid;pthread_create(&tid, nullptr, threadRoutine, (void *)"thread 1");void *ret = nullptr;pthread_join(tid, &ret);cout << "主线程正在等待...主线程成功退出,新线程的返回值:" << (long long)ret << endl;
}

线程等待是不需要关心是否有异常的,因为新线程崩溃了,主线程也崩溃了

三、pthread_exit和pthread_cancel

线程终止的话是不能用exit的,这个是终止进程的,一旦调用,exit直接把进程终止了

void pthread_exit(void *retval);
  • 参数一:用于传递线程退出时的信息

或者我们可以用这个函数进行线程取消

int pthread_cancel(pthread_t thread)
  • 参数一:线程id
  • 如果想取消谁调用这个函数填入对应的线程id就可以了
  • void *threadRoutine(void *args)
    {while (1){cout << "新线程:" << (char *)args << "running..." << endl;sleep(1);}cout << "新线程退出" << endl;
    }
    int main()
    {pthread_t tid;pthread_create(&tid, nullptr, threadRoutine, (void *)"thread 1");int count=0;while (1){cout << "main线程:"<< "running..." << endl;sleep(1);count++;if(count==5) break;}pthread_cancel(tid);cout<<"线程取消"<<tid<<endl;void *ret = nullptr;pthread_join(tid, &ret);cout << "主线程正在等待...主线程成功退出,新线程的返回值:" << (long long)ret << endl;
    }

这其中有几个细节需要注意

  •  线程被取消join的时候,退出码是-1,
  • 一般是在保证新线程运行起来了,后来不需要了才需要用这个接口
  • 不要用这个接口,用新线程取消主线程 

四、关于线程id的探索

当我们打印一个线程id之后可以发现,线程id是这个样子的,是一个非常大的整数

原因是因为它本质是一个地址!

上篇文章说过pthread会个线程提供一个用户层的栈结构,这个线程id就是栈结构的起始地址,对应的就是在库内部对应的相关属性的起始地址。

对于主线程来说直接用内核级栈结构,对于新线程来说则用的是共享区内部提供的用户层栈结构,这样就可以保证每个线程的栈是独立的了,并且还不和但执行流的进程相冲突

五、pthread_self

这个接口很简单,就是哪个线程掉的我,直接就获取对应线程的线程id

void *threadRoutine(void *args)
{while (1){cout << "新线程:" << (char *)args << "running..." << pthread_self() << endl;sleep(1);}cout << "新线程退出" << endl;
}
int main()
{pthread_t tid;pthread_create(&tid, nullptr, threadRoutine, (void *)"thread 1");printf("%lu,%p", tid, tid);cout << endl;int count = 0;while (1){cout << "main线程:"<< "running..." << pthread_self() << endl;sleep(1);count++;if (count == 5)break;}pthread_cancel(tid);cout << "线程取消" << tid << endl;void *ret = nullptr;pthread_join(tid, &ret);cout << "主线程正在等待...主线程成功退出,新线程的返回值:" << (long long)ret << endl;
}

注意:这里不推荐使用pthread_cancel调用pthread_self来自己取消自己

六、线程的局部存储

在进程中,两个进程调用同一个变量会发生写时拷贝,但是在线程中却不是这样的

int g_val = 0;void *threadRoutine(void *args)
{while (1){cout << (char *)args << " : " << g_val++ << " &: " << &g_val << endl;sleep(1);}
}
int main()
{pthread_t tid;pthread_create(&tid, nullptr, threadRoutine, (void *)"thread 1");while (1){cout << "新线程: " << g_val << "&" << &g_val << endl;sleep(1);}
}

我们的地址是一样的,所以我们的全局变量是被多线程共享的

但是也可以变成私有的,只需要在全局变量前加上__thread就可以了如:

__thread int g_val=0;

我们就可以看到每个线程都有属于自己的全局变量了

  • __thread:修饰全局变量,带来的结果就是让每一个线程各自拥有一个全局变量--线程的局部存储

七、线程调用execl

在进程时调用这个函数只是把内存中和磁盘上的数据替换掉,那么在线程中就是直接将我们所对应的代码和数据全部替换!会影响其他线程,把其他线程终止,然后直接就去调用替换的程序了,就等同于这个进程调用execl进行程序替换

八、分离线程

在进行join时,我们的主线程是必须要的等待的,也可以看到OS并没有给我们更多的选项,可是如果我们不想等待呢?那么我们就可以进行我们的分离线程

  • 默认情况下,新创建的线程是joinable的,线程退出后,需要对其进行pthread_join操作,否则无法释放资源,从而造成系统泄漏。
  • 如果不关心线程的返回值,join是一种负担,这个时候,我们可以告诉系统,当线程退出时,自动释放线程资源。
int pthread_detach(pthread_t thread);
  • 参数一:线程id

用法如下

__thread int g_val = 0;void *threadRoutine(void *args)
{pthread_detach(pthread_self());while (1){cout << (char *)args << " : " << g_val++ << " &: " << &g_val << endl;sleep(1);break;}pthread_exit((void *)10);
}
int main()
{pthread_t tid;pthread_create(&tid, nullptr, threadRoutine, (void *)"thread 1");while (1){cout << "新线程: " << g_val << "   &" << &g_val << endl;sleep(1);break;}long long n = pthread_join(tid, nullptr);cout << "n:" << n << "错误码" << strerror(n) << endl;
}

如果我们强行join的话,就会产生报错,错误码是非法的参数

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/526776.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

探索机器学习的无限可能性:从初学者到专家的旅程

探索机器学习的无限可能性&#xff1a;从初学者到专家的旅程 在当今数字时代&#xff0c;机器学习无疑是最引人注目的技术之一。它已经深入到我们生活的方方面面&#xff0c;从个性化推荐到自动驾驶汽车&#xff0c;再到医疗诊断和金融预测。但是&#xff0c;即使我们已经见证…

LangChain 教程:构建 LLM 支持的应用程序的指南

作者&#xff1a;Aditya Tripathi GPT-4 和 LLaMA 等大型语言模型 (LLM) 在过去几年中创造了一个充满可能性的世界。 它预示着人工智能工具和应用程序的繁荣&#xff0c;ChatGPT 似乎一夜之间成为家喻户晓的名字。 但如果没有为促进新一代应用程序而创建的强大工具和框架&#…

如何在Windows系统使用固定tcp公网地址ssh远程Kali系统

文章目录 1. 启动kali ssh 服务2. kali 安装cpolar 内网穿透3. 配置kali ssh公网地址4. 远程连接5. 固定连接SSH公网地址6. SSH固定地址连接测试 简单几步通过[cpolar 内网穿透](cpolar官网-安全的内网穿透工具 | 无需公网ip | 远程访问 | 搭建网站)软件实现ssh 远程连接kali! …

变频器学习

西门子变频器 SINAMICS V20 入门级变频器 SINAMICS G120C

【Godot4自学手册】第二十二节完成主人公的闪现功能

这一节我们主要自学主人公的闪现功能&#xff0c;当按下鼠标右键&#xff0c;我们的主人公根据不同的方向进行瞬间移动&#xff0c;并在身后留下一串残影&#xff0c;具体效果如下&#xff1a; 一、新建ghost场景 新建Node2D场景&#xff0c;命名为Ghost&#xff0c;存储到S…

KEIL调试模式

step1&#xff1a; step2&#xff1a; step:3推荐使用硬件的在线仿真模式 step4:启动调试模式之前需要将硬件连接STM32然后编译项目确保工程项目是没有问题的 编译结果没有问题 step5:点击keil中的放大镜图标进入调试模式 进入调试模式后界面展示 外设资源查看功能

Linux网络套接字之UDP网络程序

(&#xff61;&#xff65;∀&#xff65;)&#xff89;&#xff9e;嗨&#xff01;你好这里是ky233的主页&#xff1a;这里是ky233的主页&#xff0c;欢迎光临~https://blog.csdn.net/ky233?typeblog 点个关注不迷路⌯▾⌯ 实现一个简单的对话发消息的功能&#xff01; 目录…

Stable diffusion(一)

Stable diffusion 原理解读 名词解释 正向扩散&#xff08;Fixed Forward Diffusion Process&#xff09;&#xff1a;反向扩散&#xff08;Generative Reverse Denoising Process&#xff09; VAE&#xff08;Variational AutoEncoder&#xff09;&#xff1a;一个用于压缩图…

快速上手:使用Hexo搭建并自定义个人博客

&#x1f31f; 前言 欢迎来到我的技术小宇宙&#xff01;&#x1f30c; 这里不仅是我记录技术点滴的后花园&#xff0c;也是我分享学习心得和项目经验的乐园。&#x1f4da; 无论你是技术小白还是资深大牛&#xff0c;这里总有一些内容能触动你的好奇心。&#x1f50d; &#x…

再探再报 除 0 这件事有不同

首先&#xff0c;在数学中&#xff0c;一个数除以0是没有意义的。 其次&#xff0c;在计算机中&#xff0c;对于除零&#xff0c;传统概念里是会上报一个异常。首先是CPU内部实现会报异常。最早学组成原理和汇编的时候&#xff0c;都是说CPU寄存器中有个表示除零异常的位。在L…

上位机图像处理和嵌入式模块部署(qmacvisual二维码识别)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 如果说条形码在商品上使用比较多的话&#xff0c;那么二维码识别是一个更加使用频繁的场合。为什么使用这样频繁&#xff0c;我想很多一部分原因来…

渗透测试——信息收集

信息收集 前言 信息收集是在做渗透时找尽可能的多的信息&#xff0c;为之后的渗透做铺垫。信息收集的方法有很多 比如&#xff0c;页面、真实的IP、域名/子域名、敏感目录/文件、端口探测、CMS指纹识别、操作系统识别 1. 页面信息收集 拿到域名后&#xff0c;从网站的url中…