Linux线程(一)初识线程

目录

一、什么是线程

二、线程和进程的区别

三、线程的操作

1、创建线程

2、获取线程ID

3、线程的终止与等待

4、线程分离 


一、什么是线程

        在Linux中,线程(thread)是一种轻量级进程(Light-weight Process, LWP)的概念,它是进程内部的一个执行流,代表了程序中的一个独立执行路径。

        每个线程都拥有自己的程序计数器(PC)、栈、寄存器集合以及错误返回码(errno),但同时,线程之间共享所属进程的地址空间、文件描述符、信号处理器以及其它资源。这意味着线程可以在同一地址空间内并发执行不同的任务,有效地利用多核处理器的能力,并且线程间的通信开销比进程间通信要小得多。

从内核角度来看,线程和进程在Linux中并没有本质区别,它们都被视为任务,并由内核调度。每个线程和进程都有自己的进程控制块(PCB),在Linux中称为task_struct,但线程之间的PCB在某些方面(如内存空间)是共享的。线程的创建通常通过clone()系统调用来实现,通过传递特定的标志来决定新创建的实体与父进程之间资源共享的程度。

线程具有两个特点:

1、轻量化:创建线程更简单,因为不需要申请资源,与进程共用。

2、线程在进程的地址空间中运行。

示意图:

 

 OS如果支持线程,那么也必须管理,之前我们知道管理进程的结构PCB,但是如果管理线程再实现一个虽然可以,但是没有必要,在Linux中的实现方法是统一视为轻量级进程,这样实现更为简单。

二、线程和进程的区别

进程=内核数据结构+代码和数据

进程时承担系统资源的基本实体,线程是CPU调度的基本单位。而进程是操作系统调度的基本单位。

进程是资源分配的最小单位。每个进程都有独立的地址空间、内存、文件描述符集、打开的文件和其他资源。进程之间是隔离的,一个进程的崩溃通常不会直接影响其他进程。

线程共享其所属进程的资源,包括

文件描述符表
每种信号的处理方式(SIG_ IGN、SIG_ DFL或者自定义的信号处理函数)
当前工作目录
用户id和组id

线程不直接拥有系统资源,但可以访问其所在进程的所有资源。

每个线程有自己的

线程ID
一组寄存器
errno
信号屏蔽字
调度优先级

进程和线程的关系图: 

三、线程的操作

POSIX线程库:

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

1、创建线程

使用pthread_create()函数创建新线程。这个函数需要四个参数:指向线程标识符的指针、线程属性(通常为NULL使用默认属性)、线程入口函数的地址以及传递给线程入口函数的参数。

功能:创建一个新的线程
原型
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 ;失败返回错误码

代码示例:

#include <pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include <sys/types.h>
#include <unistd.h>
void* thread_function(void* arg);

int main() {
    pthread_t thread_id;
    int rc = pthread_create(&thread_id, NULL, thread_function, NULL);
    if (rc) {
        perror("pthread_create");
        exit(EXIT_FAILURE);
    }
    // 主线程继续执行其他任务
    while(1)
    {
    printf("i am running,pid:%d\n",getpid());
    sleep(1);
    }
    return 0;
}

void* thread_function(void* arg) {
    while(1)
    sleep(1);
    // 线程执行的代码
    return NULL;
}

 

 可以看到LWP就是线程ID,而PID=LWD的就是主线程,Linux将它们都视为轻量级进程。

2、获取线程ID

pthread_ create 函数会产生一个线程 ID ,存放在第一个参数指向的地址中。该线程 ID 和前面说的线程 ID不是一回事。
前面讲的线程 ID 属于进程调度的范畴。因为线程是轻量级进程,是操作系统调度器的最小单位,所以需要一个数值来唯一表示该线程。
pthread_ create 函数第一个参数指向一个虚拟内存单元,该内存单元的地址即为新创建线程的线程 ID ,属于NPTL 线程库的范畴。线程库的后续操作,就是根据该线程 ID 来操作线程的。
代码示例:
#include <iostream>
#include <string>
#include <functional>
#include <vector>
#include <time.h>
#include <unistd.h>
#include <pthread.h>


const int threadnum = 5;
// 新线程
void *ThreadRountine(void *args)
{
    size_t num=(size_t)args;
    while (true)
    {
        std::cout<<"i am thread"<<num<<"thread ID"<<pthread_self()<<std::endl;
        sleep(2);
    }
}
int main()
{
    std::vector<pthread_t> pthreads;
    for (size_t i = 0; i < threadnum; i++)
    {
        char threadname[64];
        snprintf(threadname, sizeof(threadname), "%s-%lu", "thread", i);

        pthread_t tid;
        pthread_create(&tid, nullptr, ThreadRountine, (void*)i);
        pthreads.push_back(tid);
        sleep(1);
    }
    std::cout << "thread id: ";
    for(const auto &tid: pthreads)
    {
        std::cout << tid << std::endl;
    }
    std::cout << std::endl;
    while (true)
    {
        std::cout << "main thread" << std::endl;
        sleep(3);
    }
}

对于Linux目前实现的NPTL实现而言,pthread_t类型的线程ID,本质 就是一个进程地址空间上的一个地址。

3、线程的终止与等待

pthread_exit函数
pthread_exit 函数 是POSIX线程库中的一个函数,用于强制退出当前调用该函数的线程。它的工作原理和作用类似于进程中的exit函数,但只影响调用它的线程,而不是整个进程。
功能:线程终止
原型
void pthread_exit(void *value_ptr);
参数
value_ptr:value_ptr不要指向一个局部变量。如果不需要传递退出状态,可以传递NULL.
返回值:无返回值,跟进程一样,线程结束的时候无法返回到它的调用者(自身)
需要注意,pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是用malloc分配的,不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函数已经退出了。
pthread_cancel函数
功能:取消一个执行中的线程
原型
int pthread_cancel(pthread_t thread);
参数
thread:线程ID
返回值:成功返回0;失败返回错误码

 pthread_join函数

功能:等待线程结束
原型
int pthread_join(pthread_t thread, void **value_ptr);
参数
thread:线程ID
value_ptr:它指向一个指针,后者指向线程的返回值
返回值:成功返回0;失败返回错误码

我们使用这段代码来测试:

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

// 线程执行的函数
void* thread_function(void* arg) {
    printf("Thread start working...\n");
    sleep(5); // 模拟工作一段时间
    // 使用pthread_exit退出线程并传递一个整数作为退出状态
    pthread_exit((void*)100); // 传递100作为退出状态
}

int main() {
    pthread_t thread_id; // 线程ID
    int* exit_status=NULL; // 用于存储线程退出状态的指针

    // 创建线程
    if(pthread_create(&thread_id, NULL, thread_function, NULL) != 0) {
        perror("pthread_create");
        exit(EXIT_FAILURE);
    }

    printf("Main thread continues doing other tasks...\n");

    // 等待线程结束并获取退出状态
    if(pthread_join(thread_id, (void**)&exit_status) != 0) {
        perror("pthread_join");
        exit(EXIT_FAILURE);
    }
    printf("Thread exited with status: %p\n", exit_status);
    return 0;
}

最后等待到进程:

当然,退出的返回值可以定义成任何东西,我们得到的应该是void*

要注意void不能定义变量,但是void*可以,因为void*本质是地址。

4、线程分离 

默认情况下,新创建的线程是joinable的,线程退出后,需要对其进行pthread_join操作,否则无法释放资源,从而造成系统泄漏。
如果不关心线程的返回值,join是一种负担,这个时候,我们可以告诉系统,当线程退出时,自动释放线程资源。
int pthread_detach(pthread_t thread);
//可以是线程组内其他线程对目标线程进行分离,也可以是线程自己分离:
pthread_detach(pthread_self());

代码示例:
我们在线程调用函数中分离这个线程:

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

// 线程执行的函数
void* thread_function(void* arg) {
    printf("Thread start working...\n");
    sleep(1); // 模拟工作一段时间
    // 使用pthread_exit退出线程并传递一个整数作为退出状态
    pthread_detach(pthread_self());
    pthread_exit((void*)100); // 传递100作为退出状态
    return NULL;
}

int main() {
    pthread_t thread_id; // 线程ID
    int* exit_status=NULL; // 用于存储线程退出状态的指针

    // 创建线程
    if(pthread_create(&thread_id, NULL, thread_function, NULL) != 0) {
        perror("pthread_create");
        exit(EXIT_FAILURE);
    }

    printf("Main thread continues doing other tasks...\n");
    sleep(1);  //一定要先分离,在等待
    // 等待线程结束并获取退出状态
    if ( pthread_join(thread_id, NULL ) == 0 ) {
    printf("pthread wait success\n");
    } else {
    printf("pthread wait failed\n");
    }
    return 0;
}

测试的时候一定要注意先分离,再等待。

joinable和分离是冲突的,一个线程不能既是joinable又是分离的。

Joinable线程:默认情况下,新创建的线程是joinable的。这意味着它可以在完成执行后被其他线程通过pthread_join函数等待并获取其退出状态。如果一个joinable线程结束了但没有其他线程调用pthread_join来等待它,那么它的资源(如栈空间)将不会被完全回收,直到某个线程成功调用了pthread_join

分离(Detached)线程:分离线程在结束时会自动释放所有资源,不需要也不应该被其他线程调用pthread_join。线程可以通过调用pthread_detach(pthread_self())函数自我分离,或者在创建时通过线程属性设置为分离状态,从而成为一个分离线程。

简而言之,一旦一个线程被标记为分离(通过调用pthread_detach或创建时设置属性),它就不再是joinable的,其他线程就不能再通过pthread_join来等待它。反之,如果一个线程保持joinable状态,那么它就应该在某个时刻被其他线程通过pthread_join等待,否则可能会造成资源泄露。

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

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

相关文章

什么样的人能上百度词条

百度百科是一个向所有互联网用户开放的平台&#xff0c;任何人都可以创建或编辑词条。然而&#xff0c;并不是所有的人物或事物都能被收录到百度百科中&#xff0c;它有一定的收录标准和审结的关于哪些人或事物能上百度百科的条件和流程。 百度百科的收录标准 知名度和影响力&…

攻击者正在利用AI,对保险公司发起大规模欺诈

保险欺诈一直是保险行业面临的重要挑战之一&#xff0c;尤其随着技术的进步&#xff0c;欺诈者也在不断更新其手段&#xff0c;利用AI技术&#xff0c;包括生成式模型、机器学习和数据分析工具等欺骗保险公司&#xff0c;而AI技术的应用正成为他们的新工具&#xff0c;使其犯罪…

深度学习:基于TensorFlow 和 Keras,使用神经网络回归模型预测 IPL 分数

前言 系列专栏&#xff1a;机器学习&#xff1a;高级应用与实践【项目实战100】【2024】✨︎ 在本专栏中不仅包含一些适合初学者的最新机器学习项目&#xff0c;每个项目都处理一组不同的问题&#xff0c;包括监督和无监督学习、分类、回归和聚类&#xff0c;而且涉及创建深度学…

性能远超GPT-4!谷歌发布Med-Gemini医疗模型;李飞飞首次创业瞄准空间智能;疫苗巨头联合OpenAl助力AI医疗...

AI for Science 企业动态速览—— * 谷歌 Med-Gemini 医疗 AI 模型性能远超 GPT-4 * 斯坦福李飞飞首次创业瞄准「空间智能」 * 疫苗巨头 Moderna 与 OpenAl 达成合作 * 美国能源部推动 AI 在清洁能源领域的应用 * 美年健康荣获「2024福布斯中国人工智能创新场景应用企业TOP10」…

Linux基础服务NFS入门篇

文章目录 Linux基础服务NFS入门篇0.前言1.NFS1.1NFS简介1.2NFS配置 Linux基础服务NFS入门篇 0.前言 本文根据大佬们的资料整理了NFS的基础知识&#xff0c; 加深对linux运维基础服务工具的理解&#xff0c;以便个人查询复习使用。 1.NFS 资料来自B站阿铭linux的印象笔记&#…

C语言 | Leetcode C语言题解之第79题单词搜索

题目&#xff1a; 题解&#xff1a; int directions[4][2] {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};bool check(char** board, int boardSize, int boardColSize, int** visited, int i, int j, char* s, int sSize, int k) {if (board[i][j] ! s[k]) {return false;} else if (…

怎么样练口才_如何练口才和反应能力?

怎么样练口才_如何练口才和反应能力&#xff1f; 要练习口才和反应能力&#xff0c;以下是一些建议的方法&#xff1a; 一、口才练习 朗读&#xff1a; 每天坚持朗读一些文章&#xff0c;可以是报纸、杂志、书籍或网络上的文章。这有助于练习口齿清晰&#xff0c;积累知识量和…

ABC352编程笔记

ABC352 编程笔记 题意&#xff1a;输入&#xff0c;四个数 a , b , c , d a,b,c,d a,b,c,d&#xff0c;若 d d d 在 c , d c,d c,d 之间&#xff0c;则输出 Yes&#xff0c;否则输出 No。 正解&#xff1a;直接判断。 #include <bits/stdc.h> //#define int long lo…

2024年去除视频水印的5种方法

如果你从事电影剪辑或者视频编辑工作&#xff0c;你经常需要从优酷、抖音、TikTok下载各种视频片段……。 通常这些视频带有水印和字幕。一些免费软件如CapCut、canva、Filmora也会给你制作的视频打上水印&#xff0c;这些水印嵌入在视频内部。 2024年去除视频水印的5种方法 …

Centos 7.9如何使用源码编译安装curl最新版本

文章目录 1、前言2、curl源代码下载3、openssl安装4、编译curl4.1、配置编译环境4.2、编译输出二进制curl程序4.3、安装编译后的curl4.4、编译完成检查4.5、验证安装 1、前言 centos 7.9&#xff0c;由于系统未2017年发行&#xff0c;且以稳定性为主&#xff0c;部分工具版本较…

Unity-NGUI爆错以后-导致不能多次点击,UI假卡死问题解决方法

太久没用&#xff0c;忘了&#xff0c;NGUI好像易出错&#xff0c;就再次点击不了 导致打开了UI关闭不了&#xff0c;每次都要重启就比较烦&#xff08;说的就是那种美术团队&#xff0c;一个 UI 打开几十层&#xff09; 就好比【左上角&#xff0c;箭头】点第二次是退出不了了…

docker安装Debian:11 freeswitch1.10.5

文章目录 一、生成一个镜像二、切换一个镜像源为阿里源三、安装一些相关依赖和freeswitch3.1第一步&#xff1a;安装freeswitch-mod和下载所需的依赖项3.2 设置密钥3.3 安装freeswitch所需的依赖项3.4 报错3.4.1 报错13.4.2 报错23.4.3 报错3 四、运行4.1 通话三十秒自动挂断 一…

verilog中输入序列不连续的序列检测

编写一个序列检测模块&#xff0c;输入信号端口为data&#xff0c;表示数据有效的指示信号端口为data_valid。当data_valid信号为高时&#xff0c;表示此刻的输入信号data有效&#xff0c;参与序列检测&#xff1b;当data_valid为低时&#xff0c;data无效&#xff0c;抛弃该时…

爬虫-无限debug场景 解决方式

解决无限debug 场景1 1. 鼠标右键 选择 continue to here&#xff08;此处不停留&#xff09;2. 鼠标右键 选择 edite breakpoint 设置 10 保证条件不成立 这行永远不执行3.方法置空 1. 方法调用加断点2. 控制台 setInterval function name() {}4. 替换文件 5. hoo…

ICode国际青少年编程竞赛- Python-2级训练场-range函数

ICode国际青少年编程竞赛- Python-2级训练场-range函数 1、 for i in range(4):Dev.turnLeft()Dev.step(i 1)2、 for i in range(4):Spaceship.step(i 1)Spaceship.turnRight()3、 for i in range(4):Spaceship.step(i 1)Dev.step(2)Dev.step(-2)4、 for i in range(1, 5)…

Dreamweaver 2021 for Mac 激活版:网页设计工具

在追求卓越的网页设计道路上&#xff0c;Dreamweaver 2021 for Mac无疑是您的梦幻之选。这款专为Mac用户打造的网页设计工具&#xff0c;集强大的功能与出色的用户体验于一身。 Dreamweaver 2021支持多种网页标准和技术&#xff0c;让您能够轻松创建符合现代网页设计的作品。其…

Androidstudio报错

现象&#xff1a; org.gradle.api.plugins.UnknownPluginException: Plugin [id: ‘com.android.application‘ 解决 方案&#xff0c;看是不是工具处于离线环境&#xff0c;是的话打开联网就好

一站式HMI软件开发套件eStation,让开发更简单高效

4月份举办的北京国际车展上全球首发车117辆&#xff0c;新能源车型278个&#xff0c;越来越多的车厂通过差异化和改善UI/UE体验&#xff0c;来获取更多用户的青睐。为快速响应差异化竞争需求&#xff0c;智能座舱HMI市场遇到以下挑战&#xff1a; 如何兼容不同项目开发人员编程…

软件设计师笔记(一)-基础要点

本文内容来自笔者学习zst 留下的笔记&#xff0c;虽然有点乱&#xff0c;但是哥已经排版过一次&#xff0c;将就着看吧&#xff0c;查缺补漏&#xff0c;希望大家都能通过&#xff0c;记得加上免费的关注&#xff01;谢谢&#xff01;csdn贴图真的很废人&#xff01; 目录 一、…

PLC设备通过智能网关采用HTTP协议JSON文件对接MES等系统平台

智能网关IGT-DSER集成了多种PLC的原厂协议&#xff0c;方便实现各种PLC、智能仪表通过HTTP协议与MES等各种系统平台通讯对接。PLC内不用编写程序&#xff0c;设备不用停机&#xff0c;通过网关的参数配置软件(下载地址)配置JSON文件的字段与PLC寄存器地址等参数即可。 智能网关…