Linux线程相关函数:线程的创建、属性、回收、退出、取消

news2024/9/20 12:16:07

1. 线程号

进程号在系统中唯一,但线程号只在其所属进程环境中有效。

(1)pthread_self函数

#include<pthread.h>

pthread_t pthread_self(void);
/*
功能:
    获取线程号
返回值:
    调用此函数线程的ID
*/

pthread_self示例:

#include<stdio.h>
#include<pthread.h>

int main(int argc, const char* argv[]) {
    pthread_t tid = 0;
    tid = pthread_self();
    printf("当前线程id:%lu.\n", tid);
    return 0;
}

编译时需要加上-pthread链接到pthread库

运行结果:

(2)pthread_equal函数

int pthraed_equal(pthread_t t1, pthread_t t2);
/*
功能:
    判断线程号t1、t2是否相等。
返回值:
    相等:非0
    不等:0
*/

pthread_equal示例:

#include<stdio.h>
#include<pthread.h>

int main(int argc, const char* argv[]) {
    pthread_t tid = 0;
    tid = pthread_self();

    if (pthread_equal(tid, pthread_self())) {
        printf("线程id相等.\n");
    } else {
        printf("线程id不等.\n");
    }
    return 0;
}

运行结果:


2. 线程的创建 

pthread_create函数

#include<pthread.h>

int pthread_create(pthread_t* thread, const pthread_attr_t* attr, 
                    void* (*start_coutine)(void*), void* arg);
/*
功能:
    创建一个线程。
参数:
    thread:线程id地址,为传出参数;
    attr:线程属性结构体,通常设置为NULL;
    start_routine:线程函数入口地址
    arg:传给线程函数的参数;
返回值:
    成功:0
    失败:非0,未设置errno,不可使用perror。
*/

pthread_create实例:

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

void* threadFunc(void* arg) { // 线程调度函数
    int var = (int)(long)(arg);
    printf("被创建线程id:%lu,传过来的参数:%d\n", pthread_self(), var);
    return NULL;
}

int main(int argc, const char* argv[]) {

    pthread_t tid;
    int ret = -1;

    // 初始化tid。因为不是所有系统中的pthread_t都是unsigned int, 因此最好使用memset初始化。
    memset(&tid, 0, sizeof(tid));

    // 创建线程
    ret = pthread_create(&tid, NULL, threadFunc, (void*)0x3);
    if (0 != ret) {
        printf("线程创建失败!\n");
        return 1;
    }
    printf("按下任意键继续...\n");
    getchar();
    printf("主线程id:%lu\n", pthread_self());

    return 0;
}

运行结果:


3. 线程属性 

typedef struct {
    int etachstate;   // 线程的分离状态
    int schedpolicy;  // 线程的调度策略
    struct sched_param schedparam; // 线程的调度参数
    int inheritsched;  // 线程的继承性
    int scope;         // 线程的作用域
    size_t guardsize;  // 线程栈末尾的警戒缓冲区大小
    int stackaddr_set; // 线程栈的设置
    void* stackaddr;   // 线程栈的位置
    size_t stacksize;  // 线程栈的大小
} pthread_attr_t;
/*
功能:
    线程属性结构体;
主要成员:
    etachstate:线程的分离状态
    guardsize:线程栈末尾的警戒缓冲区大小
    stackaddr:线程栈的位置
    stacksize:线程栈的大小

注意:
    线程属性值不能直接设置,需使用相关函数进行操作。
    如pthread_create之前用pthread_attr_init初始化,
    之后用pthread_attr_destory释放资源。
*/

(1)线程属性的初始化和销毁

#include<pthread.h>

int pthread_attr_init(pthread_attr_t* attr);
/*
功能:
    初始化线程属性attr。
参数:
    attr:待初始化的线程属性结构体。
返回值:
    成功:0
    失败:错误码
*/

int pthread_attr_destory(pthread_attr_t* attr);/*
功能:
    销毁线程属性attr。
参数:
    attr:线程属性结构体。
返回值:
    成功:0
    失败:错误码
*/

(2)线程分离状态属性设置

线程分离状态:无需其他线程阻塞等待线程结束以回收已结束线程的资源,而是让内核回收已结束线程的资源。

线程非分离状态:某个线程(如主线程)阻塞等待子线程结束以回收其资源。

#include<pthread.h>

int pthread_attr_setdetachstate(pthread_attr_t* attr, int detachstate);
/*
功能:
    设置线程属性为分离。
参数:
    attr:已初始化的线程属性结构体;
    detachstate:是否分离:
        PTHREAD_CREATE_DETACHED:分离
        PTHREAD_CREATE_JOINABLE:非分离
返回值:
    成功:0
    失败:非0
*/

int pthread_attr_getdetachstate(const pthread_attr_t* attr, int* detachstate);
/*
功能:
    获取线程是否分离状态
参数:
    attr:线程属性结构体。
        detachstate:是否分离:
        PTHREAD_CREATE_DETACHED:分离
        PTHREAD_CREATE_JOINABLE:非分离
返回值:
    成功:0
    失败:非0
*/

线程设置分离属性示例:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>

void* func(void* arg) {
    printf("子线程开始...\n");
    sleep(2);
    printf("子线程结束...\n");
    pthread_exit(NULL);
}

int main(int argc, const char* argv[]) {

    int ret = -1;
    pthread_t tid = -1;
    pthread_attr_t attr;

    // 初始化线程属性
    ret = pthread_attr_init(&attr);
    if (0 != ret) {
        printf("线程属性初始化失败。\n");
        return 1;
    }

    // 设置线程属性为分离状态
    ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    if (0 != ret) {
        printf("线程分离属性设置失败。\n");
        return 1;
    }

    // 创建线程
    ret = pthread_create(&tid, &attr, func, NULL);
    if (0 != ret) {
        printf("线程创建失败.\n");
        return 1;
    }

    sleep(3);

    // join看看是否为分离状态
    ret = pthread_join(tid, NULL);
    if (0 != ret) {
        printf("线程为分离状态,无需join.\n");
    } else {
        printf("线程为非分离状态,已被join.\n");
    }

    // 销毁线程属性
    ret = pthread_attr_destroy(&attr);
    if (0 != ret) {
        printf("线程属性销毁失败。\n");
        return 1;
    }

    return 0;
}

运行结果:

(3)线程栈大小获取和设置

#include<pthread.h>

int pthread_attr_setstacksize(pthread_attr_t* attr, size_t stacksize);
/*
功能:
    设置线程栈大小。
参数:
    attr:线程属性结构体;
    stacksize:线程栈大小;
返回值:
    成功:0;
    失败:错误码
*/

int pthread_attr_getstacksize(const pthread_attr_t* attr, size_t* stacksize);
/*
功能:
    获取线程栈大小。
参数:
    attr:线程属性结构体指针;
    stacksize:返回的线程栈大小;
返回值:
    成功:0;
    失败:错误码
*/

 4. 线程的回收

(1)pthread_join函数

主线程回收线程资源,会阻塞。

#include<pthread.h>

int pthread_join(pthread_t thread, void** retval);
/*
功能:
    类似于wait()函数。等待线程thread结束,回收线程资源;若线程已结束,则会立即返回。
参数:
    thread:等待回收的线程号;
    retval:存储进程退出状态的指针的地址;
返回值:
    成功:0
    失败:非0
*/

pthread_join示例:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>
#include<unistd.h>

void* func() {
    printf("子线程开始执行...\n");
    sleep(3);
    printf("子线程结束执行...\n");
    return (void*)0x3;
}

int main(int argc, const char* argv[]) {

    pthread_t tid;
    int ret = -1;
    void* retp = NULL;

    memset(&tid, 0, sizeof(tid));

    // 创建线程
    ret = pthread_create(&tid, NULL, func, NULL);
    if (0 != ret) {
        printf("线程创建失败.\n");
        return 1;
    }

    printf("主线程执行...\n");

    // 等待线程结束 pthread_join会阻塞
    ret = pthread_join(tid, &retp);
    if (0 != ret) {
        printf("线程join失败.\n");
        return 1;
    }

    printf("retp: %p\n", retp);
    printf("主线程退出...\n");

    return 0;
}

运行结果:

(2)pthread_detach函数

内核回收线程资源,不会阻塞。

#include<pthread.h>

int pthread_detach(pthread_t thread);
/*
功能:
    使线程thread与当前进程分离,之后线程结束后的资源回收由内核完成,因此不会阻塞。
参数:
    thread:线程号;
返回值:
    成功:0
    失败:非0
*/

 pthread_detach示例:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>

void* func(void* arg) {
    printf("子线程开始...\n");
    for (int i = 0; i < 3;++i) {
        sleep(1);
        printf("子线程工作%ds\n", i);
    }
    printf("子线程结束...\n");
    return NULL;
}

int main(int argc, const char* argv[]) {

    int ret = -1;
    pthread_t tid = -1;

    // 创建线程
    ret = pthread_create(&tid, NULL, func, NULL);
    if (0 != ret) {
        printf("线程创建失败.\n");
        return 1;
    }

    // 设置线程分离
    ret = pthread_detach(tid);
    if (0 != ret) {
        printf("线程分离失败.\n");
        return 1;
    }

    printf("主线程:按回车键退出..\n");
    getchar();

    return 0;
}

运行结果:

线程设置为detach状态,主线程不必阻塞等待回收子线程资源,而是有内核完成。


5. 线程的退出

若在线程中用exit函数退出,则导致整个进程退出,而非退出这一个线程。

如下三者可在不结束整个进程的情况下结束线程:

a)线程从执行函数中返回;

b)线程调用pthread_exit退出线程;

c)线程被同一进程中的其它线程取消。

pthread_exit函数

#include<pthread.h>

void pthread_exit(void* retval);
/*
功能:
    退出调用线程。
参数:
    retval:存储线程退出状态的指针。
*/

6. 线程的取消

pthread_cancel函数

#include<pthread.h>

int pthread_cancel(pthread_t thread);
/*
功能:
    杀死线程thread;
参数:
    thread:目标线程ID;
返回值:
    成功:0
    失败:错误码。
*/

pthread_cancel示例:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>

void* func(void* arg) {
    printf("子线程开始...\n");
    for (int i = 0; i < 5;++i) {
        sleep(1);
        printf("子线程工作%ds\n", i);
    }
    printf("子线程结束...\n");
    pthread_exit(NULL);
}

int main(int argc, const char* argv[]) {

    int ret = -1;
    pthread_t tid = -1;

    // 创建线程
    ret = pthread_create(&tid, NULL, func, NULL);
    if (0 != ret) {
        printf("线程创建失败.\n");
        return 1;
    }

    // 设置线程分离
    ret = pthread_detach(tid);
    if (0 != ret) {
        printf("线程分离失败.\n");
        return 1;
    }

    sleep(3);
    pthread_cancel(tid);  // 杀死线程

    printf("主线程:按回车键退出..\n");
    getchar();

    return 0;
}

运行结果:


7. 线程使用注意事项

(1)主线程退出,而其余线程不退出,主线程应调用pthread_exit

(2)避免僵尸线程方式:

        a)pthread_join;

        b)pthread_detach;

        c)pthread_create前设置线程分离属性;

(3)malloc和mmap申请的内存可被其他线程释放;

(4)避免在多线程中fork,除非马上使用exec;

(5)避免多线程中使用信号机制。

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

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

相关文章

【Unity-UGUI控件全面解析】| Panel 容器组件详解

🎬【Unity-UGUI控件全面解析】| Panel 容器组件详解一、组件介绍二、组件属性面板三、代码操作组件💯总结🎬 博客主页:https://xiaoy.blog.csdn.net 🎥 本文由 呆呆敲代码的小Y 原创,首发于 CSDN🙉 🎄 学习专栏推荐:Unity系统学习专栏 🌲 游戏制作专栏推荐:游…

2007-2020年上市公司数字经济化指标含原始数据和计算代码(do文档)

2007-2020年上市公司数字经济化指标含原始数据和计算代码&#xff08;do文档&#xff09; 1、时间&#xff1a;2007-2020 2、范围&#xff1a;A股上市公司剔除jin rong行业 3、数据说明&#xff1a;包含计算过程和原始数据 4、参考文献&#xff1a;祁怀锦,数字经济对公司治…

利用C#实现动态替换桌面快捷方式对应的应用程序

公司有一个特殊的业务可能会用到这个&#xff0c;至于什么业务就不展开了。本文的内容作为备用方案。 实现思路&#xff1a; 1 获取当前exe程序运行的全路径 2 获取桌面的所有快捷方式 3 遍历快捷方式&#xff0c;获取快捷键方式对应程序的运行路径&#xff0c;并与当前…

【开发工具】 今天我要教会你安装Office 2021 RTM 专业增强零售版 你开不开心

&#x1f680; 个人主页 极客小俊 ✍&#x1f3fb; 作者简介&#xff1a;web开发者、设计师、技术分享博主 &#x1f40b; 希望大家多多支持一下, 我们一起进步&#xff01;&#x1f604; &#x1f3c5; 如果文章对你有帮助的话&#xff0c;欢迎评论 &#x1f4ac;点赞&#x1…

统一请求封装和pytest入门使用

统一请求封装解决的问题 解决多个py文件共享session的问题 统计请求的数据 统一异常处理 统一日志监控 封装 在项目文件中创建一个common文件夹&#xff0c; 用来进行统一接口封装 import requestsclass RequestUtil:# 调用底层的requests.session()方法进行封装sess r…

超详细的R语言svykm函数绘制复杂抽样设计数据cox回归生存曲线(Kaplan-Meier)

我们在既往的文章《R语言绘制复杂抽样设计数据cox回归生存曲线(Kaplan-Meier)》中介绍了怎么使用jskm包的svykm函数绘制复杂抽样设计数据cox回归生存曲线(Kaplan-Meier)&#xff0c;但是有粉丝觉得讲得不够详细&#xff0c;希望讲得详细一点&#xff0c;今天我们继续来介绍一下…

flinkCDC在Delta.io中是什么 是CDF

类似flink CDC databricks 官方文档: How to Simplify CDC With Delta Lakes Change Data Feed - The Databricks Blog delta.io 官方文档: Change data feed — Delta Lake Documentation 概述 更改数据馈送 (CDF) 功能允许 Delta 表跟踪 Delta 表版本之间的行级更改 在…

vue+element-plus角色权限管理分配

这里的图片是截图这个老师的项目 在src/uitls/permission.js加入以下内容 本段代码讲解: 参数一:后台传来的路由 参数二:前端所有的路由 先遍历前端所有路由,在里面继续遍历后台路由,通过二者某一个关键字的是否相同判断用户是否有权限(这里老师使用的是title关键字),关键字相…

21安徽练习

题目分为4部分 APK 集群 流量 exe 我尽量都做一下&#xff0c;逆向不是很会&#xff0c;就当提升自己。 [填空题]请获取app安装包的SHA256校验值&#xff08;格式&#xff1a;不区分大小写&#xff09;&#xff08;10分&#xff09; e15095d49efdccb0ca9b2ee125e4d8136cac5…

树莓派Pico W无线开发板MQTT协议通信MicroPython编程实践

本博文介绍采用ThonnyMicroPython和umqtt.simple库MQTTClient类的对象方法编制树莓派Pico W无线开发板MQTT协议通信程序&#xff0c;将Pico W无线开发板、电脑或Android手机无线连接到远程MQTT服务器&#xff0c;给出采用电脑MQTTX应用程序及手机Android MQTT客户端App远程控制…

用python的QT做界面

文章目录 入口文件界面参数调整数据从dat解析出来的文件从界面点击打开文件夹的功能实现主要功能代码网络参数存图替换功能&#xff0c;比如把倒频谱替换成倒频谱2 入口文件 入口文件&#xff0c;主要用来实例化窗口&#xff08;不重要&#xff09;&#xff0c;只要知道从这里…

每日学术速递4.30

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CV 1.Masked Frequency Modeling for Self-Supervised Visual Pre-Training(ICLR 2023) 标题&#xff1a;用于自监督视觉预训练的掩蔽频率建模 作者&#xff1a;Jiahao Xie, Wei Li, Xi…

SwiftUI 设计和调试复杂界面的基本技巧示例

功能需求 对于比较复杂的 SwiftUI 界面,我们需要在充分了解 SwiftUI 各个视图基本特性的同时,合理利用 Xcode 强大的预览(Preview)机制,实时且全面的测试所有场景下的显示情况。 如上图所示:我们在 App 支持的每种语言环境中都对界面进行了全面的测试,并解决了 Cell 里…

使用Lambda表达式mutable有感

使用Lambda表达式mutable有感 在学Qt的时候&#xff0c;我看着mutable陷入了沉思&#xff0c;总觉得它和C中的某个特性很像&#xff1f;没错&#xff0c;就是 深拷贝 如果你忘记了深拷贝和浅拷贝是什么&#xff0c;那跟着我来一起回忆一下吧。首先来看深拷贝与浅拷贝的概念 浅…

从零开始学习Linux运维,成为IT领域翘楚(四)

文章目录 &#x1f525;Linux路径&#x1f525;Linux处理文件目录的常用命令&#x1f525;Linux文件编辑工具vi/vim&#x1f525;Linux文件内容查看命令 &#x1f525;Linux路径 路径&#xff0c;顾名思议&#xff0c;是指从树形目录中的某个目录层次到某个文件的一条道路。 Li…

设计模式-工厂、装饰模式

一. 设计模式-工厂模式 package com.factory.pattern;/**目标&#xff1a;工厂模式。什么是工厂设计模式&#xff1f;工厂模式&#xff08;Factory Pattern&#xff09;是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式&#xff0c;它提供了一种创建对象的方…

Spring 5 笔记 - AOP

1. AOP 简介 AOP&#xff1a;Aspect Oriented Programming&#xff0c; 面向切面编程或面向方面编程。利用AOP可以对业务逻辑的各个部分进行隔离&#xff0c;从而使得业务逻辑各部分之间的耦合度降低&#xff0c;提高程序的可重用性&#xff0c;同时提高了开发的效率。 将日志…

【英语】大学英语CET考试,写作部分(论述文+应用文,6篇范文)

文章目录 3项评分标准&#xff08;内容&结构&#xff0c;语言&#xff09;0.1 论述文个人小结 1、论述文&#xff1a;审题与功能句2、论述文&#xff1a;修饰内容和名言模板3、论述文&#xff1a;现象作文&利弊分析4、论述文&#xff1a;给出权威论据和有侧重的现象5、…

新老stp的配置和安全总结部分

老stp只有根桥没有备份桥 老stp的五种接口状态&#xff1a; disable 接口down没开stp blocking 阻塞 listening 发bpdu&#xff0c;比较bpdu优劣 leraning 开始学习mac地址表 forwardding 转发 老stp直接拓扑变化30秒&#xff0c;间接拓扑变化50秒 RSTP只有3种端口状态&#…

每日学术速递4.28

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CV 1.StepFormer: Self-supervised Step Discovery and Localization in Instructional Videos(CVPR 2023) 标题&#xff1a;StepFormer&#xff1a;教学视频中的自我监督步骤发现和定位…