【Linux】—— 线程控制的基本介绍

目录

(一)POSIX线程库

(二)创建线程

2.1 线程ID及进程地址空间布局

(三)线程终止

(四)分离线程


(一)POSIX线程库

POSIX线程库(POSIX Threads Library),通常简称为Pthreads,是一个为POSIX操作系统(包括UNIX和类UNIX系统如Linux)提供线程支持的库。Pthreads定义了一组API,允许程序员创建、管理、同步和销毁线程。 

  • 与线程有关的函数构成了一个完整的系列,绝大多数函数的名字都是以“pthread_”打头的
  • 要使用这些函数库,要通过引入头文<pthread.h>
  • 链接这些线程函数库时要使用编译器命令的“-lpthread”选项

(二)创建线程

【函数介绍】 

功能:创建一个新的线程

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

参数
    thread:返回线程ID
    attr:设置线程的属性,attr为NULL表示使用默认属性
    start_routine:是个函数地址,线程启动后要执行的函数
    arg:传给线程启动函数的参数

返回值:成功返回0;失败返回错误码

错误检查

  1. 对于Pthreads函数,如果调用失败,通常会返回一个非零的错误代码,你可以使用这个错误代码来查询具体是什么错误发生了。为了将错误代码映射到人类可读的错误消息,你可以使用strerrorpthread_strerror函数(如果可用)。

  2. 另外,Pthreads库确实为每个线程提供了一个私有的errno变量,这允许线程安全地访问errno。然而,对于Pthreads函数本身来说,直接检查返回值通常是更好的做法,因为这样可以避免任何可能的线程间干扰,并且通常更加高效。

 【代码示例】 

#include <iostream>
#include <unistd.h>
#include <pthread.h>

using namespace std;

int g_val = 0; 

void *threadRun1(void *args)
{
    while (true)
    {
        sleep(1);
        cout << "t1 thread..." << getpid() << " &g_val: " << &g_val << " , g_val: " << g_val << endl;
    }
}

void *threadRun2(void *args)
{
    while (true)
    {
        sleep(1);
        cout << "t2 thread..." << getpid()  << " &g_val: " << &g_val << " , g_val: " << g_val++ << endl;
    }
}

int main()
{
    pthread_t t1, t2;

    pthread_create(&t1, nullptr, threadRun1, nullptr);
    pthread_create(&t1, nullptr, threadRun2, nullptr);

    while (true)
    {
        sleep(1);
        cout << "main thread..." << getpid()  << " &g_val: " << &g_val << " , g_val: " << g_val << endl;
    }
}

2.1 线程ID及进程地址空间布局

  1. pthread_ create函数会产生一个线程ID,存放在第一个参数指向的地址中。该线程ID和前面说的线程ID不是一回事。
  2. 前面讲的线程ID属于进程调度的范畴。因为线程是轻量级进程,是操作系统调度器的最小单位,所以需要一个数值来唯一表示该线程。
  3. pthread_ create函数第一个参数指向一个虚拟内存单元,该内存单元的地址即为新创建线程的线程ID,属于NPTL线程库的范畴。线程库的后续操作,就是根据该线程ID来操作线程的。
  4. 线程库NPTL提供了pthread_ self函数,可以获得线程自身的ID

【函数介绍】 

pthread_t pthread_self(void);

错误检查

  • 如果成功,pthread_self 返回调用线程的线程 ID。如果发生错误,这个函数没有定义错误码返回机制,因为它总是应该成功。

  【代码示例】 

#include <iostream>  
#include <pthread.h>  
#include <unistd.h>

using namespace std;

void* threadRun(void* arg) {  
    pthread_t id = pthread_self(); // 获取当前线程的 ID  
    cout << "the thread ID is :  " << id << endl;  
  
    sleep(1);  
  
    return nullptr;  
}  
  
int main() {  
    pthread_t tid;  
    int ret;  
  
    // 创建线程  
    ret = pthread_create(&tid, nullptr, threadRun, nullptr);  
    if (ret != 0) {  
        cerr << "Error: pthread_create() failed with error " << ret << endl;  
        return 1;  
    }  
  
    // 等待线程完成  
    pthread_join(tid, nullptr);  
  
    cout << "Main thread exiting." << endl;  
    return 0;  
}

 pthread_t 到底是什么类型呢?取决于实现。对于Linux目前实现的NPTL实现而言,pthread_t类型的线程ID,本质就是一个进程地址空间上的一个地址


(三)线程终止

如果需要只终止某个线程而不终止整个进程 , 可以有三种方法 :
  • 1. 从线程函数return。这种方法对主线程不适用,main函数return相当于调用exit
#define NUM 10

void *threadrun(void *args)
{
    char *name = (char*)args;
    while (true)
    {
        cout << "new thread run, the new thread name is : " << name   << endl;
        sleep(3);
        break;
    }
    delete name;
    return  nullptr;
}

int main()
{
    pthread_t tids[NUM];
    for(int i= 0; i< NUM; ++i){
        char *tname = new char[64];
        snprintf(tname,64,"thread-%d",i+1);
        pthread_create(tids+i,nullptr,threadrun,tname);
    }

    void *ret = nullptr;
    for(int i= 0; i< NUM; ++i){
        int n = pthread_join(tids[i],nullptr);
        if(n != 0) cerr << "pthread_join error" << endl;

        cout << "thread quit: " <<(uint64_t)ret << endl;
    }
    cout << "all thread quit..."<<endl; 

    return 0;
}
  • 2. 线程可以调用pthread_ exit终止自己。

【函数介绍】 

功能:线程终止
原型
    void pthread_exit(void *value_ptr);
参数
    value_ptr:value_ptr不要指向一个局部变量。

返回值:无返回值,跟进程一样,线程结束的时候无法返回到它的调用者(自身)
  • 需要注意,pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是用malloc分配的,不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函数已经退出了。

 【代码示例】 

#define NUM 10

void *threadrun(void *args)
{
    char *name = (char*)args;
    while (true)
    {
        cout << "new thread run, the new thread name is : " << name   << endl;
        sleep(3);
        break;
    }
    delete name;
    pthread_exit((void*)1); 
}

int main()
{
    pthread_t tids[NUM];
    for(int i= 0; i< NUM; ++i){
        char *tname = new char[64];
        snprintf(tname,64,"thread-%d",i+1);
        pthread_create(tids+i,nullptr,threadrun,tname);
    }

    void *ret = nullptr;
    for(int i= 0; i< NUM; ++i){
        int n = pthread_join(tids[i],nullptr);
        if(n != 0) cerr << "pthread_join error" << endl;

        cout << "thread quit: " <<(uint64_t)ret << endl;
    }
    cout << "all thread quit..."<<endl; 

    return 0;
}
  • 3. 一个线程可以调用pthread_ cancel终止同一进程中的另一个线程。

【函数介绍】 

功能:取消一个执行中的线程
原型
    int pthread_cancel(pthread_t thread);
参数
    thread:线程ID

返回值:成功返回0;失败返回错误码

  【代码示例】 

void *threadRun(void* args)
{
    const char*name = static_cast<const char *>(args);

    int cnt = 5;
    while(cnt)
    {
        cout << name << " is running: " << cnt-- << " obtain self id: " << pthread_self() << endl;
        sleep(1);
    }

    pthread_exit((void*)11); 

    // PTHREAD_CANCELED; #define PTHREAD_CANCELED ((void *) -1)
}

int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, threadRun, (void*)"thread 1");
    sleep(3);

    pthread_cancel(tid);

    void *ret = nullptr;
    pthread_join(tid, &ret);
    cout << " new thread exit : " << (int64_t)ret << "quit thread: " << tid << endl;
    return 0;
}

【输出结果】(三秒后取消操作)


其次就是在上述代码中,使用了线程等待函数,那为什么需要线程等待?  

  • 已经退出的线程,其空间没有被释放,仍然在进程的地址空间内。
  • 创建新的线程不会复用刚才退出线程的地址空间。

 【函数介绍】

功能:等待线程结束

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

参数
thread:线程ID
value_ptr:它指向一个指针,后者指向线程的返回值

返回值:成功返回0;失败返回错误码
调用该函数的线程将挂起等待 , 直到 id thread 的线程终止。 thread 线程以不同的方法终止 , 通过 pthread_join 得到的终止状态是不同的,总结如下:
  • 1. 如果thread线程通过return返回,value_ ptr所指向的单元里存放的是thread线程函数的返回值。
  • 2. 如果thread线程被别的线程调用pthread_ cancel异常终掉,value_ ptr所指向的单元里存放的是常数PTHREAD_ CANCELED。
  • 3. 如果thread线程是自己调用pthread_exit终止的,value_ptr所指向的单元存放的是传给pthread_exit的参数。
  • 4. 如果对thread线程的终止状态不感兴趣,可以传NULLvalue_ ptr参数。


(四)分离线程

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

 【函数介绍】

int pthread_detach(pthread_t thread);

【代码示例】 

void* threadRun(void* args) {   
    sleep(2);  
    cout << "线程任务已完成\n";  
    return nullptr;  
}  
  
int main() {  
    pthread_t tid;  
    int ret;  
  
    // 创建线程  
    ret = pthread_create(&tid, nullptr, threadRun, nullptr);  
    if (ret) {  
        cerr << "Error: pthread_create() failed\n";  
        return 1;  
    }  
  
    // 分离线程  
    pthread_detach(tid);  
  
    // 主线程继续执行  
    cout << "主线程继续执行...\n";  
  
    // 主线程休眠一段时间以便观察分离线程的输出  
    sleep(3);  
  
    // 主线程结束,分离线程的资源会在其完成后由系统自动回收  
    return 0;  
}

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

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

相关文章

PySpark教程(001):基础准备与数据输入

PySpark 学习目标 了解什么是Spark、PySpark了解为什么学习PySpark了解如何和大数据开发方向进行衔接 Spark是什么&#xff1f; Apache Spark是用于大规模数据处理的统一分析引擎。 简单来说&#xff0c;Spark是一款分布式的计算框架&#xff0c;用于调度成百上千的服务器…

Go微服务: 理解分布式锁

概述 我们先看一个场景&#xff0c;到了双11&#xff0c;我们的商户又开始卖商品啦但是&#xff0c;我们的库存是有限的&#xff0c;如果超卖了&#xff0c;可能平台就会涉及相关法律责任了所以&#xff0c;我们的库存扣除问题&#xff0c;一定是一个非常经典的问题 先看上图&…

LabVIEW电池测试系统

1. 背景 随着电动汽车、可再生能源等领域的迅速发展&#xff0c;电池作为能源储存和释放的核心组件&#xff0c;其性能评估变得尤为重要。电池的充放电性能、容量、循环寿命等参数直接影响着设备的工作性能和使用寿命。因此&#xff0c;设计一套全面、准确的电池测试系统对于提…

orbslam2代码解读(2):tracking跟踪线程

书接上回&#xff0c;mpTracker->GrabImageMonocular(im,timestamp)函数处理过程&#xff1a; 如果图像是彩色图&#xff0c;就转成灰度图如果当前帧是初始化的帧&#xff0c;那么在构建Frame的时候&#xff0c;提取orb特征点数量为正常的两倍&#xff08;目的就是能够在初…

Android JobService启动系统源码分析

以下就JobService的执行流程,系统层实现进行详解 入口点在JobScheduler.scheduler 系统层JobScheduler是个抽象类,它的实现类是JobScheduler mBinder,一看就知道这里面肯定是跨进程了。它的服务端在JobSchedulerService里面,具体 为什么请看系统服务器启动流程相关文章,…

【传知代码】上下位关系自动检测方法(论文复现)

前言&#xff1a;在信息爆炸的时代&#xff0c;我们每天都沉浸在海量的数据和信息中。随着互联网技术的飞速发展&#xff0c;如何从这些信息中准确、高效地提取出有用的知识&#xff0c;成为了当下研究的热点。其中&#xff0c;上下位关系&#xff08;也称为层级关系或种属关系…

康姿百德集团公司官网床垫价格透明,品质睡眠触手可及

选择康姿百德床垫&#xff0c;价格透明品质靠谱&#xff0c;让你拥有美梦连连 在当今社会&#xff0c;良好的睡眠质量被越来越多的人所重视。睡眠不仅关系到我们第二天的精力状态&#xff0c;更长远地影响着我们的身体健康。因此&#xff0c;选择一款合适的床垫对于获得优质睡…

Android Studio Jellyfish版本修改project使用特定jdk版本的步骤

android studio总是把这些东西改来改去让人十分恼火&#xff0c;IDE本身改来改去就让人无法上手就立即工作&#xff0c;很多时间浪费在IDE和gradle的配置和奇奇怪怪现象的斗智斗勇上&#xff0c;搞Android是真的有点浪费生命。一入此坑深不见底 jellyfish版安卓studio已经无法通…

Python模块导入,别out了,看看这些高级玩法!

目录 1、基础导入&#xff1a;import语句 &#x1f4da; 1.1 直接导入模块 1.2 导入模块别名 1.3 从模块导入特定属性 2、高级导入&#xff1a;from...import &#x1f9f0; 2.1 选择性导入模块成员 2.2 嵌套模块导入 2.3 避免命名冲突策略 3、动态导入&#xff1a;imp…

Mysql基础-多表查询

Mysql基础-多表查询 文章目录 Mysql基础-多表查询1 多表关系1.1 一对多1.2 多对多1.3 一对一 2 多表查询概述2.1 多表查询分类 3 内连接4 外连接5 自连接6 联合查询-union union all7 子查询7.1 标量子查询7.2 列子查询7.3 行子查询7.4 表子查询 1 多表关系 项目开发中&#xf…

Jenkins构建打包部署前端Vue项目至Nginx

一. 安装jenkins 基于DockerJenkins实现自动部署SpringBootMaven项目-CSDN博客 二. 安装NodeJs插件并配置 显示上面两行则表示安装成功, 然后回到首页, 点击’系统管理’->‘全局工具配置’: 配置node.js 三. 创建jenkins项目 1、创建项目 2、配置gitee 3、配置源码 4、…

网络学了点socket,写个聊天室,还得改进

目录 第一版: common 服务端: 客户端 第一版问题总结: 第二版 服务端: 客户端: 改进: Windows客户端 一些小问题 还可以进行的改进 这篇文章我就先不讲网络基础的东西了,我讲讲在我进行制作我这个拉跨聊天室中遇到的问题,并写了三版代码. 第一版: common #pragm…

MyBatis映射器:实现动态SQL语句

大家好&#xff0c;我是王有志&#xff0c;一个分享硬核 Java 技术的金融摸鱼侠&#xff0c;欢迎大家加入 Java 人自己的交流群“共同富裕的 Java 人”。 上一篇文章中&#xff0c;我们已经学习了如何在 MyBatis 的映射器中通过简单的 SQL 语句实现增删改查&#xff0c;今天我…

关闭windows11磁盘地址栏上的历史记录

关闭windows11的磁盘地址栏上的历史记录 windows11打开磁盘后访问某一个磁盘路径后会记录这个磁盘路径&#xff0c;而且有时候会卡住这个地址栏&#xff08;关都关不掉&#xff09;&#xff0c;非常麻烦。 如下图所示&#xff1a; 关闭地址栏历史记录 按下windows键打开开…

C++面试八股文:static和const的关键字有哪些用法?

100编程书屋_孔夫子旧书网 某日二师兄参加XXX科技公司的C工程师开发岗位第7面&#xff1a; 面试官&#xff1a;C中&#xff0c;static和const的关键字有哪些用法? 二师兄&#xff1a;satic关键字主要用在以下三个方面&#xff1a;1.用在全局作用域&#xff0c;修饰的变量或者…

Adobe Premiere Pro 2024下载安装(视频剪辑软件Pr2024)

百度网盘下载地址&#xff08;含PR教学课程&#xff08;PR从入门到精通108节课程&#xff09;&#xff09;https://pan.baidu.com/s/1WKYZENoMzTcKhbgMgbEPGQ?pwdSIMS 一、Pr简介 Pr全称Premiere&#xff0c;是Adobe公司开发的一款功能强大的视频剪辑软件&#xff0c;目前被…

Java实现物候相机和植被分析导出相对指数成果图

一、基础概念 植被分析是利用地理信息系统&#xff08;GIS&#xff09;、遥感技术、生态学、环境科学等多学科交叉手段&#xff0c;对植被的分布、类型、结构、组成、动态变化、生产力、生态功能进行量化评估的过程。植被分析对于生态保护、生物多样性研究、资源管理、环境监测…

Mysql基础 - 事务

Mysql基础 - 事务 文章目录 Mysql基础 - 事务1 事务简介2 事务操作2.1 控制事务一2.2 控制事务二 3 事务四大特性4 并发事务问题5 事务隔离级别 1 事务简介 事务是一组操作的集合&#xff0c;他是一个不可分割的工作单位&#xff0c;事务会把所有操作作为一个整体一起向系统提…

[chisel]马上要火的硬件语言,快来了解一下优缺点

Chisel是什么&#xff1f; Chisel的全称为Constructing Hardware In a Scala Embedded Language&#xff0c;是一个基于Scala的DSL&#xff08;Domain Specific Language&#xff0c;特定领域专用语言&#xff09;。2012年&#xff0c;加州大学伯克利分校&#xff08;UC Berkel…

【Rd-03E】使用CH340给Rd03_E雷达模块烧录固件

Rd03_E 指导手册 安信可新品雷达模组Rd-03搭配STM32制作简易人体感应雷达灯教程 http://t.csdnimg.cn/mqhkE 测距指导手册网址&#xff1a; https://docs.ai-thinker.com/_media/rd-03e%E7%B2%BE%E5%87%86%E6%B5%8B%E8%B7%9D%E7%94%A8%E6%88%B7%E6%89%8B%E5%86%8C%E4%B8%AD%…