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

news2025/1/23 17:42:02

目录

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

达梦查询表字段详细信息脚本(字段名称、描述、类型、长度及是否为空)

达梦查询表字段详细信息脚本&#xff08;字段名称、描述、类型、长度及是否为空&#xff09; 该SQL 脚本&#xff0c;用于查询表中字段的基本信息&#xff0c;包括字段名称、描述、数据类型、数据长度、是否为空及是否为主键等属性。 SQL 脚本 -- 输入变量 DECLAREp_owner VA…

学习笔记073——Java中的【Object】和【包装类】

文章目录 1、Object 类1.1、什么是 Object 类1.2、可能被重写的常用方法 2、包装类2.1、什么是包装类&#xff1f;2.2、装箱和拆箱 1、Object 类 1.1、什么是 Object 类 Java 通过类来构建代码的结构&#xff0c;类分为两种&#xff1a; 1、Java 提供的工具类&#xff0c;不…

面向预测性维护的TinyML技术栈全面综述

论文标题&#xff1a;A Holistic Review of the TinyML Stack for Predictive Maintenance&#xff08;面向预测性维护的TinyML技术栈全面综述&#xff09; 作者信息&#xff1a;Emil Njor, Mohammad Amin Hasanpour, Jan Madsen, Xenofon Fafoutis&#xff0c;均来自丹麦技术…

【MySQL】InnoDB引擎中的Compact行格式

目录 1、背景2、数据示例3、Compact解释【1】组成【2】头部信息【3】隐藏列【4】数据列 4、总结 1、背景 mysql中数据存储是存储引擎干的事&#xff0c;InnoDB存储引擎以页为单位存储数据&#xff0c;每个页的大小为16KB&#xff0c;平时我们操作数据库都是以行为单位进行增删…

【SpringAOP】深入浅出SpringAOP从原理到源码

AOP对象是如何创建的 对于熟悉Spring IOC流程源码的同学来说&#xff0c;一定了解bean的整个生命周期&#xff0c;也就是从实例化、属性填充、初始化三个过程。那么对于Bean 工厂来说&#xff0c;是如何保证需要创建代理的对象创建代理的呢。 从图中可以看到&#xff0c;本质…

VMware虚拟机Ubuntu 18.04版本 磁盘扩容

一、版本配置 虚拟机版本&#xff1a;VMware WORKSTATION 16 PRO Ubuntu版本&#xff1a;Ubuntu 18.04 二、磁盘大小介绍 目的&#xff1a;磁盘扩容&#xff08;20G----->100G&#xff09;&#xff0c;从20G扩到100G 查看磁盘大小命令&#xff1a;df -h 扩容前的磁盘大小 …

QT多线程(二):基于互斥锁与读写锁的线程同步

此处需要说明的是&#xff0c;这里的线程同步概念与操作系统中的线程同步并无区别&#xff0c;都是避免多个线程同时访问临界区数据可能产生的读写错误问题。在 Qt 中&#xff0c;有多个类可以实现线程同步的功能&#xff0c;这些类包括 QMutex、QMutexLocker、 QReadWriteLock…

【ChatGPT】解锁AI思维链:如何让机器像人类一样思考?

在人工智能领域&#xff0c;我们一直在追求让机器像人类一样思考。然而&#xff0c;即使是最先进的AI&#xff0c;也常常被诟病缺乏“常识”&#xff0c;难以理解复杂问题&#xff0c;更不用说像人类一样进行逻辑推理和解决问题了。最经常的表现就是遇到不会的地方&#xff0c;…

重新定义页签!Choerodon UI Tabs让管理更高效

01 引言 Tabs 组件通过提供平级区域&#xff0c;将大块内容进行有效的收纳和展现&#xff0c;从而保持界面整洁。但在企业应用的快速发展中&#xff0c;这样传统的页签组件已无法满足我们对界面布局和个性化展示的追求。Choerodon UI Tabs 组件通过支持多级分组、个性化配置、…

机器学习之偏差

机器学习中的偏差&#xff08;Bias&#xff09;是指模型的预测值与真实值之间的系统性误差&#xff0c;或者说模型无法准确捕捉数据中复杂模式的能力。偏差通常与模型的假设或学习能力有关&#xff0c;过高的偏差会导致模型的性能不佳&#xff0c;表现为欠拟合。 偏差的来源 模…

SSH连接监控以及新用户创建和系统资源访问限制

目录 监控连接数SSH连接数的限制和影响理论限制可能的影响 创建SSH新用户为每个ssh用户配置系统资源限制1. 使用 /etc/security/limits.conf 限制资源2. 使用 cgroups 控制资源3. 磁盘配额限制4. 限制 SSH 访问5. 使用 PAM 限制6. 监控脚本示例7. 设置定期任务清理8. 检查配置是…

测试工程师八股文04|计算机网络 和 其他

一、计算机网络 1、http和https的区别 HTTP和HTTPS是用于在互联网上传输数据的协议。它们都是应用层协议&#xff0c;建立在TCP/IP协议栈之上&#xff0c;用于客户端&#xff08;如浏览器&#xff09;和服务器之间的通信。 ①http和https的主要区别在于安全性。http是一种明…

单片机学习笔记——入门51单片机

一、单片机基础介绍 1.何为单片机 单片机&#xff0c;英文Micro Controller Unit&#xff0c;简称MCU 。内部集成了中央处理器CPU、随机存储器ROM、只读存储器RAM、定时器/计算器、中断系统和IO口等一系列电脑的常用硬件功能 单片机的任务是信息采集&#xff08;依靠传感器&a…

【青牛科技】D8563是低功耗的CMOS实时时钟/日历电路,它提供一个可编程时钟输出,一个中断输出和掉电检测器,所有的地址和数据通过IC总线接口串行传递。

概述&#xff1a; D8563是低功耗的CMOS实时时钟/日历电路,它提供一个可编程时钟输出&#xff0c;一个中断输出和掉电检测器&#xff0c;所有的地址和数据通过IC总线接口串行传递。最大总线速度为400Kbitss每次读写数据后&#xff0c;内嵌的字地址寄存器会自动产生增量。 主要特…

安卓获取所有可用摄像头并指定预览

在Android设备中&#xff0c;做预览拍照的需求的时候&#xff0c;我们会指定 CameraSelector DEFAULT_FRONT_CAMERA前置 或者后置CameraSelector DEFAULT_BACK_CAMERA 如果你使用的是平板或者工业平板&#xff0c;那么就会遇到多摄像头以及外置摄像头问题&#xff0c;简单的指…

R语言学习笔记-1

1. 基础操作和函数 清空环境&#xff1a;rm(list ls()) 用于清空当前的R环境。 打印输出&#xff1a;print("Hello, world") 用于输出文本到控制台。 查看已安装包和加载包&#xff1a; search()&#xff1a;查看当前加载的包。install.packages("package_na…

Windows如何安装go环境,离线安装beego

一、安装go 1、下载go All releases - The Go Programming Language 通过网盘分享的文件&#xff1a;分享的文件 链接: https://pan.baidu.com/s/1MCbo3k3otSoVdmIR4mpPiQ 提取码: hxgf 下载amd64.zip文件&#xff0c;然后解压到指定的路径 2、配置环境变量 需要新建两个环境…

Mac上使用ln指令创建软链接、硬链接

在Mac、Linux和Unix系统中&#xff0c;软连接&#xff08;Symbolic Link&#xff09;和硬连接&#xff08;Hard Link&#xff09;是两种不同的文件链接方式。它们的主要区别如下&#xff1a; 区别&#xff1a; 硬连接&#xff1a; 不能跨文件系统。不能链接目录&#xff08;为…

Unity A*算法实现+演示

注意&#xff1a; 本文是对基于下方文章链接的理论&#xff0c;并最终代码实现&#xff0c;感谢作者大大的描述&#xff0c;非常详细&#xff0c;流程稍微做了些改动&#xff0c;文末有工程网盘链接&#xff0c;感兴趣的可以下载。 A*算法详解(个人认为最详细,最通俗易懂的一…

博弈论3:图游戏SG函数(Graph Games)

目录 一、图游戏是什么 1.游戏特征 2.游戏实例 二、图游戏的必胜策略 1.SG 函数&#xff08;Sprague-Grundy Function&#xff09; 2.必胜策略&#xff08;利用SG函数&#xff09; 3.拿走游戏转化成图游戏&#xff08;Take-away Game -> Graph Game&#xff09; 一、图…