Linux -- 线程控制相关的函数

目录

pthread_create -- 创建线程

参数

返回值

 代码 -- 不传 args:

编译时带  -lpthread

运行结果 

为什么输出混杂?

如何证明两个线程属于同一个进程?

 

如何证明是两个执行流? 

什么是LWP?

代码 -- 传 args:

运行结果:

pthread_self -- 线程标识符

代码:

LWP标识符 和 线程标识符的区别

pthread_join -- 等待线程退出

 前言:主线程比新线程先退出

参数

返回值

 代码 -- 不获取退出状态

代码 -- 获取退出状态

 ​编辑

pthread_exit -- 终止线程

前言:新、主线程共享地址空间

参数

作用

代码 

pthread_cancel -- 取消线程

参数

返回值

代码


pthread_create -- 创建线程

#include <pthread.h>

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                  void *(*start_routine) (void *), void *arg);

参数

thread: 指向一个 pthread_t 类型的指针,用于存储新创建线程的标识符,是输出型参数

attr: 指向一个 pthread_attr_t 类型的指针,这个参数可以用来设置线程的属性,如栈大小、调度策略等。如果不需要特别设置线程属性,可以传递 NULL

start_routine: 这是一个函数指针,指向新线程开始执行的函数。该函数必须接受一个 void* 类型的参数,并返回一个 void* 类型的结果。

arg: 这个参数将被传递给 start_routine 函数作为其唯一的参数。如果你不想传递任何参数,可以使用 NULL

返回值

如果函数调用成功,返回值为 0

如果发生错误,返回值为一个非零的错误代码

 代码 -- 不传 args:

#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;

void* newthreadRun(void* args)
{
    while(1)
    {
        cout<<"I am new thread"<<endl;
        sleep(1);
    }
}
int main()
{
    pthread_t tid;
    pthread_create(&tid,nullptr,newthreadRun,nullptr);
    while(1)
    {
        cout<<"I am main thread"<<endl;
        sleep(1);
    }
    return 0;
}

编译时带  -lpthread

这里需要了解一点小故事,用户知道线程和进程,但不知道轻量级进程,而Linux中没有真线程,Linux中没有线程相关的系统调用,只有轻量级进程的系统调用,为了让用户和系统达成一致,系统将轻量级进程的系统调用进行封装,转换成线程相关的接口语义提供给用户,也就有了pthread库(即原生线程库),这个库既不属于C语言,也不属于C++,所以在Linux系统中编写多线程时,都必须在编译时带上 -lpthread。

在 Linux 中编译使用 Pthreads 的程序时,通常需要链接 Pthreads 库。这可以通过编译命令中添加 -lpthread 选项来实现。-lpthread 选项不仅告诉编译器链接 Pthreads 库,还会启用一些必要的编译器选项,以确保线程支持的正确性和性能。 如果不使用 -lpthread 选项,编译器可能会报错或生成不可用的二进制文件。

thread:thread.cc
	g++ -o $@ $^ -std=c++11 -lpthread

.PHONY:clean
clean:
	rm -f thread

运行结果 

可以看出,两个执行流同时输出信息。同时也可以看出,一开始新、主线程打印的消息混在一起了,后面才分开来,这是正常现象。

为什么输出混杂?

在多线程程序中,多个线程同时向终端输出信息时,可能会出现输出混杂的情况。这是因为每个线程的输出操作并不是原子的(原子操作,即要么完全执行,要么根本不执行),即一个线程可能在另一个线程已经开始输出但还没有完成输出时就开始了自己的输出,这种现象通常称为“输出交错”或“输出混杂”。 

如何证明两个线程属于同一个进程?

不传 args 版本的代码运行时,输入命令 ps ajx | head -1 && ps ajx | grep thread 筛选出 thread 进程,可以看出只有一个进程被调度

如何证明是两个执行流? 

当代码运行起来时,输入命令 ps -aL | head -1 && ps -aL | grep thread 可以查看线程的信息,可以看出两个线程的 pid 一样,即属于同一个进程,而 LWP 不同,则说明一个进程中有两个执行流

ps -aL-a显示所有进程,包括其他用户的进程,-L 显示每个线程的详细信息。 

什么是LWP?

LWP(Light Weight Process)是轻量级进程的缩写,在 Linux 中,LWP 通常被称为“线程”

代码 -- 传 args:

#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;
#include<string>

string toHex(pthread_t tid)
{
    char buffer[64];
    snprintf(buffer,sizeof(buffer),"0x%lx",tid);
    return buffer;
}
void* newthreadRun(void* args)
{
    std:string threadname=(char*)args;
    int cnt=5;
    while(cnt--)
    {
       std::cout<<threadname<<" is running: "<<cnt<<", pid: "<<getpid()<<" mythread id: "<<toHex(pthread_self())<<std::endl;
       sleep(1);
    }
    return nullptr;
}
int main()
{
    pthread_t tid;
    pthread_create(&tid,nullptr,newthreadRun,(void*)"thread-1");
    
    pthread_join(tid,nullptr);
    return 0;
}

运行结果:

pthread_self -- 线程标识符

#include <pthread.h>
pthread_t pthread_self(void);

该函数用于获取当前线程的标识符pthread_t 类型)。

代码:

#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;
#include<string>

string toHex(pthread_t tid)
{
    char buffer[64];
    snprintf(buffer,sizeof(buffer),"0x%lx",tid);
    return buffer;
}
void* newthreadRun(void* args)
{
    while(1)
    {
        cout<<"I am new thread, new thread tid: "<<toHex(pthread_self())<<", pid: "<<getpid()<<endl;
        sleep(1);
    }
}
int main()
{
    pthread_t tid;
    pthread_create(&tid,nullptr,newthreadRun,nullptr);
    while(1)
    {
        cout<<"I am main thread, main thread tid: "<<toHex(pthread_self())<<", pid: "<<getpid()<<endl;
        sleep(1);
    }
    return 0;
}

可以看出,新、主线程的线程标识符 tid 的值不一样,同时也可以看出,LWP 和线程标识符 tid的值是不一样的

LWP标识符 和 线程标识符的区别

 LWP(Light Weight Process)标识符和线程标识符(Thread Identifier,TID)在数值上通常是不一样的。虽然它们在概念上密切相关,但它们表示的是不同的标识符,用途和获取方式也有所不同。

pthread_join -- 等待线程退出

 前言:主线程比新线程先退出

#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;
#include<string>

string toHex(pthread_t tid)
{
    char buffer[64];
    snprintf(buffer,sizeof(buffer),"0x%lx",tid);
    return buffer;
}
void* newthreadRun(void* args)
{
    std:string threadname=(char*)args;
    int cnt=5;//新线程运行5秒
    while(cnt--)
    {
       std::cout<<threadname<<" is running: "<<cnt<<", pid: "<<getpid()<<" mythread id: "<<toHex(pthread_self())<<std::endl;
       sleep(1);
    }
    return nullptr;
}
int main()
{
    pthread_t tid;
    pthread_create(&tid,nullptr,newthreadRun,(void*)"thread-1");
    
    sleep(1);//因为主线程没有阻塞等待新线程,1秒后,主线程先退出了
    std::cout<<"main thread quit"<<std::endl;
    return 0;
}

因为主线程没有阻塞等待新线程退出,1秒后,主线程退出了,主线程退出了就等同于整个进程退出了,分配给进程的资源都被释放了,所以所有的线程都要退出,所以新线程还没执行完就被退出了,通常需要主线程最后结束。线程的退出也需要wait,不然会发生内存泄漏问题。


#include <pthread.h>

int pthread_join(pthread_t thread, void **value_ptr);

该函数用于等待指定的线程终止,并获取该线程的退出状态。 

参数

thread:要等待的线程的标识符pthread_t 类型,可以调用 pthread_self 函数获取)。

value_ptr输出型参数,用于存储线程的退出状态。如果不需要获取退出状态,可以传递 NULL

返回值

等待线程退出成功返回 0。 

等待线程退出失败返回非零错误码

 代码 -- 不获取退出状态

#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;
#include<string>

string toHex(pthread_t tid)
{
    char buffer[64];
    snprintf(buffer,sizeof(buffer),"0x%lx",tid);
    return buffer;
}
void* newthreadRun(void* args)
{
    std:string threadname=(char*)args;
    int cnt=5;
    while(cnt--)
    {
       std::cout<<threadname<<" is running: "<<cnt<<", pid: "<<getpid()<<" mythread id: "<<toHex(pthread_self())<<std::endl;
       sleep(1);
    }
    return nullptr;
}
int main()
{
    pthread_t tid;
    pthread_create(&tid,nullptr,newthreadRun,(void*)"thread-1");
    
    int n=pthread_join(tid,nullptr);//获取返回值
    std::cout<<"main thread quit, n="<<n<<std::endl;
   
    return 0;
}

新线程正常退出,故返回值为 0. 

代码 -- 获取退出状态

#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;
#include<string>

string toHex(pthread_t tid)
{
    char buffer[64];
    snprintf(buffer,sizeof(buffer),"0x%lx",tid);
    return buffer;
}
void* newthreadRun(void* args)
{
    std:string threadname=(char*)args;
    int cnt=5;
    while(cnt--)
    {
       std::cout<<threadname<<" is running: "<<cnt<<", pid: "<<getpid()<<" mythread id: "<<toHex(pthread_self())<<std::endl;
       sleep(1);
    }
    return (void*)123;
}
int main()
{
    pthread_t tid;
    pthread_create(&tid,nullptr,newthreadRun,(void*)"thread-1");

    void* ret=nullptr;
    int n=pthread_join(tid,&ret);
    //ret强转为long long是为了避免精度损失
    std::cout<<"main thread quit, n="<<n<<",main thread get a ret:"<<(long long)ret<<std::endl;

    return 0;
}

线程的退出状态其实就是线程执行的任务函数的返回值。 

 

pthread_exit -- 终止线程

前言:新、主线程共享地址空间

#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;
#include<string>
int g_val=100;
string toHex(pthread_t tid)
{
    char buffer[64];
    snprintf(buffer,sizeof(buffer),"0x%lx",tid);
    return buffer;
}
void* newthreadRun(void* args)
{
    std:string threadname=(char*)args;
    int cnt=5;
    while(cnt--)
    {
       printf("new thread, g_val:%d,&g_val:%p\n",g_val,&g_val);
       g_val++;//在新线程中改变g_val的值
       sleep(1);
    }
    return nullptr;
}

int main()
{
    pthread_t tid;
    pthread_create(&tid,nullptr,newthreadRun,(void*)"thread-1");

    int cnt=10;
    while(cnt--)
    {
        //主线程不改变g_val的值
        printf("main thread, g_val:%d,&g_val:%p\n",g_val,&g_val);
        sleep(1);
    }

    void* ret=nullptr;
    int n=pthread_join(tid,&ret);
    std::cout<<"main thread quit, n="<<n<<",main thread get a ret:"<<(long long)ret<<std::endl;
   
    return 0;
}

可以看出,新线程修改了 g_val 的值,主线程中 g_val 的值也被修改了,说明新、主线程共享了地址空间,看到的是同一个变量,而不是和进程一样,发生写时拷贝。

#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;
#include<string>
int g_val=100;
string toHex(pthread_t tid)
{
    char buffer[64];
    snprintf(buffer,sizeof(buffer),"0x%lx",tid);
    return buffer;
}
void* newthreadRun(void* args)
{
    std:string threadname=(char*)args;
    int cnt=5;
    while(cnt--)
    {
       printf("new thread, g_val:%d,&g_val:%p\n",g_val,&g_val);
       g_val++;
       //故意对空指针进行解引用
       int *p=nullptr;
       *p=10;
       sleep(1);
    }
    return nullptr;
}

int main()
{
    pthread_t tid;
    pthread_create(&tid,nullptr,newthreadRun,(void*)"thread-1");

    int cnt=10;
    while(cnt--)
    {
        printf("main thread, g_val:%d,&g_val:%p\n",g_val,&g_val);
        sleep(1);
    }

    void* ret=nullptr;
    int n=pthread_join(tid,&ret);
    std::cout<<"main thread quit, n="<<n<<",main thread get a ret:"<<(long long)ret<<std::endl;
   
    return 0;
}

 

在新线程中故意对野指针进行解引用,结果新、主线程一起退出了,这是因为在同一个进程中运行的所有线程共享相同的地址空间,这意味着如果一个线程造成了段错误(segmentation fault),那么这个错误会影响到整个进程,而不仅仅是单个线程。操作系统通常会终止整个进程以防止进一步的损坏


#include <pthread.h>

void pthread_exit(void *value_ptr);

参数

value_ptr:一个指向指针的指针,用于存储线程的退出状态

这个值可以被 pthread_join 函数捕获并使用。如果不需要传递退出状态,可以传递 NULL

作用

终止当前线程:调用 pthread_exit 的线程会立即终止其执行

传递退出状态:可以通过 value_ptr 参数传递一个退出状态,这个状态可以被 pthread_join 函数捕获。

代码 

#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;
#include<string>
int g_val=100;
string toHex(pthread_t tid)
{
    char buffer[64];
    snprintf(buffer,sizeof(buffer),"0x%lx",tid);
    return buffer;
}
void* newthreadRun(void* args)
{
    std:string threadname=(char*)args;
    int cnt=5;
    while(cnt--)
    {
       printf("new thread, g_val:%d,&g_val:%p\n",g_val,&g_val);
     
       g_val++;
       
       sleep(1);
    }
    pthread_exit((void*)123);
}

int main()
{
    pthread_t tid;
    pthread_create(&tid,nullptr,newthreadRun,(void*)"thread-1");

    void* ret=nullptr;
    int n=pthread_join(tid,&ret);
    std::cout<<"main thread quit, n="<<n<<",main thread get a ret:"<<(long long)ret<<std::endl;
   
    return 0;
}

pthread_cancel -- 取消线程

#include <pthread.h>

int pthread_cancel(pthread_t thread);

参数

thread:要取消的线程的标识符pthread_t 类型)。

返回值

取消线程成功,返回 0

取消线程失败,返回非零错误码

代码

#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;
#include<string>
int g_val=100;
string toHex(pthread_t tid)
{
    char buffer[64];
    snprintf(buffer,sizeof(buffer),"0x%lx",tid);
    return buffer;
}
void* newthreadRun(void* args)
{
    std:string threadname=(char*)args;
    int cnt=5;
    while(cnt--)
    {
       printf("new thread, g_val:%d,&g_val:%p\n",g_val,&g_val);
       
       g_val++;
      
       sleep(1);
    }
    pthread_exit((void*)123);
}

int main()
{
    pthread_t tid;
    pthread_create(&tid,nullptr,newthreadRun,(void*)"thread-1");

    pthread_cancel(tid);

    void* ret=nullptr;
    int n=pthread_join(tid,&ret);
    std::cout<<"main thread quit, n="<<n<<",main thread get a ret:"<<(long long)ret<<std::endl;
   
    return 0;
}

线程的退出状态为 -1,表示线程被取消。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

VTK知识学习(26)- 图像基本操作(一)

1、前言 图像处理离不开一些基本的图像数据操作&#xff0c;例如获取和修改图像的基本信息、访问和修改图像像素值、图像显示、图像类型转换等。熟练掌握这些基本操作有助于使用 VTK进行图像处理应用程序的快速开发。 2、图像信息的访问与修改 1&#xff09;利用vtkIamgeData…

【WPF】把DockPanel的内容生成图像

要在WPF中将一个 DockPanel 的内容生成为图像并保存&#xff0c;可以按照与之前类似的步骤进行&#xff0c;但这次我们将专注于 DockPanel 控件而不是整个窗口。 DockPanel的使用 WPF&#xff08;Windows Presentation Foundation&#xff09;中的 DockPanel 是一种布局控件&…

【Linux】处理用户输入

一、基本介绍 1、如何传递参数 向shell脚本传递数据的最基本方法就是通过命令行参数。如下&#xff0c;这条命令会向test.sh脚本传递10和20这两个参数。 ./test.sh 10 20 2、如何读取参数 bash shell会将所有的命令行参数都指派给称作位置参数&#xff08;positional parame…

SpringBoot+Vue3实现阿里云视频点播 实现教育网站 在上面上传对应的视频,用户开会员以后才能查看视频

要使用阿里云视频点播&#xff08;VOD&#xff09;实现一个教育网站&#xff0c;其中用户需要成为会员后才能查看视频&#xff0c;这个过程包括上传视频、设置权限控制、构建前端播放页面以及确保只有付费会员可以访问视频内容。 1. 视频上传与管理 创建阿里云账号&#xff…

POI-TL插件开发-表格分组插件

POI-TL版本&#xff1a;1.12.2 改造于&#xff1a;LoopRowTableRenderPolicy 模板设计&#xff1a; 分组之前&#xff1a; 分组之后&#xff1a; 代码实现&#xff1a; public class LoopRowGroupTableRenderPolicy implements RenderPolicy {private String prefix;privat…

发送webhook到飞书机器人

发送webhook到飞书机器人 参考链接 自定义机器人使用指南 创建自定义机器人 邀请自定义机器人进群。 进入目标群组&#xff0c;在群组右上角点击更多按钮&#xff0c;并点击 设置。 在右侧 设置 界面&#xff0c;点击 群机器人。 在 群机器人 界面点击 添加机器人。 在 添…

36. Three.js案例-创建带光照和阴影的球体与平面

36. Three.js案例-创建带光照和阴影的球体与平面 实现效果 知识点 Three.js基础 WebGLRenderer WebGLRenderer 是Three.js中最常用的渲染器&#xff0c;用于将场景渲染到网页上。 构造器 new THREE.WebGLRenderer(parameters)参数类型描述parametersobject可选参数&#…

Mybatis分页插件的使用问题记录

项目中配置的分页插件依赖为 <dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper</artifactId><version>5.1.7</version></dependency>之前的项目代码编写分页的方式为&#xff0c;通过传入的条件…

RIP---路由信息协议

动态路由 自治系统 ---AS 由单一的机构或组织所管理的一系列 IP 网络设备的集合 。 AS 编号&#xff1a; ASN----1-65535----IANA &#xff08;互联网数字分配机构&#xff09; AS 的通讯方式 AS 内部 ---- 运行相同的路由协议 ---- 内部网关协议&#xff08; IGP &#x…

NLP 分词技术浅析

一、NLP 分词技术概述 &#xff08;一&#xff09;定义 自然语言处理&#xff08;NLP&#xff09;中的分词技术是将连续的文本序列按照一定的规则切分成有意义的词语的过程。例如&#xff0c;将句子 “我爱自然语言处理” 切分为 “我”、“爱”、“自然语言处理” 或者 “我…

排序算法:冒泡排序

每一次顺序便遍历&#xff0c;比较相邻的两个元素&#xff0c;交换。 void bubbleSort(vector<int>&v) { int n v.size();//元素个数 //外层j控制的是待排序区间的长度 for (int j n;j > 1;j--) { bool flag 0;//提高效率&#xff0c;判断比较好了就结束 /…

抽象之诗:C++模板的灵魂与边界

引言 在计算机科学的浩瀚长河中&#xff0c;C模板如同一颗璀璨的星辰&#xff0c;以其独特的泛型编程方式为程序设计注入了灵魂。它是抽象的艺术&#xff0c;是类型的舞蹈&#xff0c;是效率与灵活性的交响乐。模板不仅是一种技术工具&#xff0c;更是一种哲学思考&#xff0c…

Linux通信System V:消息队列 信号量

Linux通信System V&#xff1a;消息队列 & 信号量 一、信号量概念二、信号量意义三、操作系统如何管理ipc资源&#xff08;2.36版本&#xff09;四、如何对信号量资源进行管理 一、信号量概念 信号量本质上就是计数器&#xff0c;用来保护共享资源。多个进程在进行通信时&a…

day4:tomcat—maven-jdk

一&#xff0c;java项目部署过程 编译&#xff1a;使用javac命令将.java源文件编译成.class宇节码文件打包&#xff1a;使用工具如maven或Gradle将项目的依赖、资源和编译后的字节码打包成一个分发格式&#xff0c;如.jar文件&#xff0c;或者.war文件(用于web应用&#xff09…

提炼关键词的力量:AI驱动下的SEO优化策略

内容概要 在当今数字化营销的环境中&#xff0c;关键词对于提升网站的可见性和流量起着至关重要的作用。企业和个人必须重视有效的关键词策略&#xff0c;以便在竞争激烈的网络市场中脱颖而出。本文将深入探讨如何利用人工智能技术来优化SEO策略&#xff0c;特别是在关键词选择…

仓鼠身长能长到多少厘米?

仓鼠&#xff0c;作为颇受欢迎的宠物&#xff0c;其小巧玲珑的身形是吸引众多饲主的重要原因之一。那么&#xff0c;仓鼠的身长究竟能长到多少厘米呢&#xff1f;这背后其实蕴含着不少有趣的知识。 一般而言&#xff0c;常见的仓鼠品种如三线仓鼠、紫仓仓鼠等&#xff0c;成年…

八大设计模式

设计模式在日常软件开发中的重要性 目录 单例模式工厂模式策略模式代理模式观察者模式装饰器模式模板方法模式建造者模式总结 单例模式 单例模式确保一个类只有一个实例&#xff0c;通常用于管理共享资源&#xff0c;如配置、缓存、线程池等。 代码实现&#xff1a;双重检查…

直流充电桩基本工作原理

1、控制导引电路 2、电动汽车直流快充工作原理 1&#xff09;第一阶段 未充电自然状态阶段 充电枪处于自然阶段&#xff0c;充电枪上的按钮没有按下&#xff0c;也就是电路图中的开关S处于接通状态&#xff0c;此时R1 、 R2串联&#xff0c;检测点1处的电压为6V 2&#xff09;…

c4d动画怎么导出mp4视频,c4d动画视频格式设置

宝子们&#xff0c;今天来给大家讲讲 C4D 咋导出mp4视频的方法。通过用图文教程的形式给大家展示得明明白白的&#xff0c;让你能轻松理解和掌握&#xff0c;不管是理论基础&#xff0c;还是实际操作和技能技巧&#xff0c;都能学到&#xff0c;快速入门然后提升自己哦。 c4d动…

【原生js案例】ajax的简易封装实现后端数据交互

ajax是前端与后端数据库进行交互的最基础的工具&#xff0c;第三方的工具库比如jquery,axios都有对ajax进行第二次的封装&#xff0c;fecth是浏览器原生自带的功能&#xff0c;但是它与ajax还是有区别的&#xff0c;总结如下&#xff1a; ajax与fetch对比 实现效果 代码实现 …