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

server/2024/12/20 6:30:15/

目录

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.ccg++ -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,表示线程被取消。


http://www.ppmy.cn/server/151639.html

相关文章

监听ECharts图表dataZoom动态显示图表内容

需求 从接口获取到84条数据&#xff0c;要求一开始全显示的时候不显示inside的label&#xff0c;不管怎么滑动dataZoom的x轴&#xff0c;只要图表上的数据小于20条&#xff0c;就需要显示inside的label&#xff0c;否则不显示 1、第一时间肯定要监听dataZoom的事件&#xff0c;…

TCP常见问题

文章目录 一、两种状态图二、常见问题1、MSL是什么 3、为何等待2MSL3、为何三次握手&#xff0c;不握手、握手一次、两次行吗4、为何四次挥手&#xff0c;三次行吗&#xff0c;两次行吗 一、两种状态图 四次挥手 二、常见问题 1、MSL是什么 MSL是Maximum Segment Lifetime的英…

python飞机大战游戏.py

python飞机大战游戏.py import pygame import random# 游戏窗口大小 WINDOW_WIDTH 600 WINDOW_HEIGHT 800# 颜色定义 BLACK (0, 0, 0) WHITE (255, 255, 255)# 初始化Pygame pygame.init()# 创建游戏窗口 window pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))…

0基础学java之Day29(单例模式、死锁)

单例模式 理解&#xff1a;在整个项目中&#xff0c;该类的实例只能有一个 1.饿汉式 优点&#xff1a;线程安全 缺点&#xff1a;浪费资源 public class A { ​private static A a new A();private A(){}public static A getInstance(){return a;}public static void met…

【C语言】头文件”“和<>的详解

前言 作者在刚开始学C语言的时候&#xff0c;都是用的< >去引用头文件&#xff0c;但在学习STM32的时候发现&#xff0c;程序中大量使用" "去引用双引号。 那么二者有什么区别呢? 无论使用哪种方式&#xff0c;头文件的目的都是为了引用你需要的文件供你编程使…

网络安全(5)_访问控制列表ACL

6. 访问控制列表ACL 6.1 标准访问控制列表 &#xff08;1&#xff09;标准ACL ①标准ACL是基于IP数据包的源IP地址作为转发或是拒绝的条件。即&#xff0c;所有的条件都是基于源IP地址的。 ②基本不允许或拒绝整个协议组。它不区分IP流量类型。如Telnet、UDP等服务。 &#…

工作与学习方向

从去年9月开始线上实习&#xff0c;实习了6个月。 今年6月开始工作&#xff0c;在临实习期被辞退。 现在又在新公司干了三个月。 工作中哪些点于我是重要的呢&#xff1f; 代码规范,比如命名规范&#xff0c;以及一些写程序时需要注意的点。 需要好好学习设计模式&#xff0c;虽…

什么是单页面应用,有哪些特点

单页面应用 (Single Page Applications, SPAs) 的定义与特点 一、定义 单页面应用&#xff08;Single Page Application&#xff0c;简称 SPA&#xff09;是一种 Web 应用程序类型&#xff0c;它在用户与应用交互时不会重新加载整个网页。相反&#xff0c;SPA 使用 JavaScrip…