Linux线程(一)初识线程

news2024/12/23 11:13:37

目录

一、什么是线程

二、线程和进程的区别

三、线程的操作

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等待,否则可能会造成资源泄露。

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

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

相关文章

五一超级课堂---Llama3-Tutorial(Llama 3 超级课堂)---第一节 Llama 3 本地 Web Demo 部署

课程文档&#xff1a; https://github.com/SmartFlowAI/Llama3-Tutorial 课程视频&#xff1a; https://space.bilibili.com/3546636263360696/channel/collectiondetail?sid2892740&spm_id_from333.788.0.0 操作平台&#xff1a; https://studio.intern-ai.org.cn/consol…

全面解析C++11与C++20线程(含内容)

昨晚跟一些小伙伴做了第一次直播尝试&#xff0c;一起探讨了C11 thread与 C20的jthread&#xff0c;于此同时给大家出了几个问题&#xff0c;在直播之外不会公布答案&#xff0c;所以以后直播还是得跟着走起。 总共有22人参加直播&#xff0c;氛围相当不错&#xff0c;没有录播…

Linux 无名信号量(Semaphore)的使用

目录 一、无名信号量的概念二、无名信号量相关函数三、信号量的使用步骤四、应用场景五、测试代码 一、无名信号量的概念 Linux无名信号量&#xff08;Semaphore&#xff09;   在Linux操作系统中&#xff0c;信号量&#xff08;Semaphore&#xff09;是一种用于进程间或线程…

sqlite3命令行工具无法退出问题处理

一、背景&#xff1a; 软件使用的后台数据库为sqlite&#xff0c;linux主机系统层面使用sqlite3命令行工具登录数据库后&#xff0c;无法执行sql脚本&#xff0c;无法退出sqlite3。无法执行ctrlc&#xff0c;执行ctrlz后sqlite3前台进程被中断&#xff0c;但是该进程没有退出。…

字节发布文生图模型PuLID:高效身份ID特征定制,单张图像克隆AI虚拟分身

前言 字节研究团队近日提出了一种新型的文生图身份ID定制方法PuLID(Pure and Lightning ID Customization)。相较于传统的微调方法&#xff0c;PuLID无需复杂的参数优化就可以实现高效的身份ID定制&#xff0c;且能最大程度减少对原始模型行为的干扰。 PuLID是通过将轻量级的…

docker学习-docker常用其他命令整理

随便写写&#xff0c;后面有空再更新 镜像命令&#xff0c;容器命令已在之前略有更新&#xff0c;这次不写&#xff0c; 一、后台启动命令 # 命令 docker run -d 容器名 # 例子 docker run -d centos # 启动centos&#xff0c;使用后台方式启动 # 问题&#xff1a; 使用doc…

Electron学习笔记(一)

文章目录 相关笔记笔记说明 一、轻松入门 1、搭建开发环境2、创建窗口界面3、调试主进程 二、主进程和渲染进程1、进程互访2、渲染进程访问主进程类型3、渲染进程访问主进程自定义内容4、渲染进程向主进程发送消息5、主进程向渲染进程发送消息6、多个窗口的渲染进程接收主进程发…

OSError: image file is truncated (36 bytes not processed)解决方案

错误原因&#xff1a; 图像文件被损坏 解决方案&#xff1a; 代码开头添加如下两行代码&#xff1a; from PIL import ImageFile ImageFile.LOAD_TRUNCATED_IMAGES True

[华为OD] B卷 树状结构查询 200

题目&#xff1a; 通常使用多行的节点、父节点表示一棵树&#xff0c;比如 西安 陕西 陕西 中国 江西 中国 中国 亚洲 泰国 亚洲 输入一个节点之后&#xff0c;请打印出来树中他的所有下层节点 输入描述 第一行输入行数&#xff0c;下面是多行数据&#xff0c;每行以空…

远程开机与远程唤醒BIOS设置

远程开机与远程唤醒BIOS设置 在现代计算机应用中&#xff0c;远程管理和控制已成为许多企业和个人的基本需求。其中&#xff0c;远程开机和远程唤醒是两项非常实用的功能。要实现这些功能&#xff0c;通常需要在计算机的BIOS中进行一些特定的设置。以下是对远程开机和远程唤醒…

《深入解析阿里电商:数据中台与业务的双中台系统架构》

阿里巴巴作为全球领先的电商巨头&#xff0c;其技术架构一直是业界关注的焦点。特别是阿里电商的双中台系统架构——数据中台和业务中台&#xff0c;这两个平台在提升数据处理效率和业务响应速度方面起到了至关重要的作用。本文将深入探讨这一架构的设计理念、核心组件及其在实…

Androidstudio报错

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

【新手指南】Android Studio中应用App的相关配置

前言&#xff1a; 注意这是一个对于Android开发入门学习者而言的一个教程&#xff0c;因为自己平时很少使用Android进行原生应用的开发&#xff0c;对于使用Android Studio配置Android App应用的一些参数&#xff08;如版本号&#xff0c;应用包名&#xff0c;应用图标&#x…

MySQL变量的声明与使用

MySQL变量的声明与使用 1、标识符不能以数字开头 2、自能使用_或$符号&#xff0c;不允许使用其他符号。 3、不允许使用系统关键字 将赋值与查询结合 set userName 刘德华; select userName: 刘青云; # 将赋值与查询结合 查询变量/使用变量 select userName as 读取到的u…

Python批量备份华为设备配置到FTP服务器

Excel表格存放交换机信息&#xff1a; 备份文件夹效果图&#xff1a; Windows系统配置计划任务定时执行python脚本&#xff1a; Program/script&#xff1a;C:\Python\python.exe Add arguments (optional)&#xff1a; D:\Python_PycharmProjects\JunLan_pythonProje…

[C/C++] -- DFS搜索迷宫路径

设计一个程序&#xff0c;能够对给定的迷宫进行路径搜索&#xff0c;并输出一条从起点到终点的路径。具体来说&#xff0c;程序需要实现以下功能&#xff1a; 接受用户输入的迷宫地图&#xff0c;包括迷宫的行数和列数&#xff0c;以及每个格子的状态&#xff08;0 表示可通行…

十四五”智慧城市:视频大数据汇聚系统2.0建设方案与特点分析

一、背景需求分析 随着科技的不断发展&#xff0c;智慧城市的建设已经成为城市发展的重要方向。视频汇聚系统作为智慧城市建设的重要组成部分&#xff0c;已经得到了广泛的应用和推广。视频汇聚系统是智慧城市中非常重要的组成部分&#xff0c;它利用摄像头和传感器技术来收集…

Python | Leetcode Python题解之第80题删除有序数组中的重复项II

题目&#xff1a; 题解&#xff1a; class Solution:def removeDuplicates(self, nums: List[int]) -> int:idx, left, right 0, 0, 0while left < len(nums):nums[idx] nums[left]idx 1while right < len(nums) and nums[right] nums[left]:right 1if right - …

【深度学习】时空图卷积网络(STGCN),预测交通流量

论文地址&#xff1a;https://arxiv.org/abs/1709.04875 Spatio-Temporal Graph Convolutional Networks: A Deep Learning Framework for Traffic Forecasting 文章目录 一、摘要二、数据集介绍美国洛杉矶交通数据集 METR-LA 介绍美国加利福尼亚交通数据集 PEMS-BAY 介绍美国…

ASP.NET校园新闻发布系统的设计与实现

摘 要 校园新闻发布系统是在学校区域内为学校教育提供资源共享、信息交流和协同工作的计算机网络信息系统。随着网络技术的发展和Internet应用的普及&#xff0c;互联网已成为人们获取信息的重要来源。由于现在各大学校的教师和学生对信息的需求越来越高&#xff0c;校园信息…