linux环境下线程的介绍和POSIX线程接口应用实例

news2024/11/17 5:50:19

目录

概述

1 线程概念

1.1 线程的特性

1.2 线程的运行状态

2 线程API

2.1 pthread的数据类型

2.2 pthread函数的返回值

2.3 POSIX线程接口

2.3.1 创建线程函数pthread_create

2.3.2 终止线程

2.3.3 线程ID

2.3.4 连接已终止线程

2.3.5 线程分离

3 线程VS进程

4 线程使用的案例

4.1 使用pthread_exit退出线程

4.1.1 功能介绍

4.1.2 编写代码

4.1.3 验证

4.2 修改线程属性

4.2.1 功能介绍

4.2.2 编写代码

4.2.3 验证

5 参考文献


概述

本文详细介绍线程的相关知识,并介绍和线程相关的pthread的API,对其中重要的函数做了详细的说明,还比较了线程和进程的优缺点。最后,使用pthread API编写了两个案例,以更好的认识和理解这些接口函数。

1 线程概念

每一个进程有一个地址空间和一个控制线程。同一个地址空间准并行(对于单核CPU来说,本质上还是串行执行的)运行多个控制线程,这些线程就像分离的进程。所以,线程也可以被称作mini进程。下图可形象的表示进程和线程的关系:

1.1 线程的特性

1)线程依附于进程存在,当该进程消亡后,运行在该进程下的线程也全部消亡;

2)同一个进程下的所有线程,共享同一个地址空间和所有可用数据;

3)线程是轻量级别的运行程序(和进程比较),它们比进程更容易创建和销毁;

4)在存在大量计算和大量IO处理的系统中,多线程允许这些活动重叠进行,从而加快应用程序的执行速度;

5)进程用于把资源集中在一起,线程则是在CPU上被调度执行的实体;

6)同一个进程中,允许运行多个线程。

1.2 线程的运行状态

一个线程可以处在若干中状态中的任何一个:运行态,阻塞态,就绪态或者终止态

运行态:正在运行的线程,拥有CPU的资源。并且活跃执行。

阻塞态:线程被阻塞,以便等待其他事件或者资源来释放它的

就绪态:线程可以被调度,只要轮到它就马上被执行

终止态:线程停止运行,并且不再被唤醒

2 线程API

2.1 pthread的数据类型

数据类型描述
pthread_t线程ID
pthread_mutex_t互斥对象
pthread_mutexattr_t互斥属性对象
pthread_cond_t条件变量
pthread_condattr_t条件变量的属性对象
pthread__key_t线程特有数据的键
pthread_once_t一次初始化控制上下文
pthread_attr_t线程的属性对象

2.2 pthread函数的返回值

pthread函数返回0,表示成功,返回一个正值表示失败。这只是为了与使用 errno 到的函数进行兼容,在线程中,从函数中返回错误码更为清晰整洁,不需要依赖那些随着函数执行不断变化的全局变量,这样可以把错误的范围限制在引起出错的函数中 。

2.3 POSIX线程接口

为了实现可移植的线程程序,IEEE标准定义了线程的标准。这里介绍一些使用线程常用到的一些接口函数

函数名描述
pthread_create创建一个新线程
pthread_exit结束调用的线程
pthread_join等待一个特定的线程退出
pthread_yield释放CPU来运行另外一个线程
pthread_attr_init创建并初始化一个线程的属性结构
pthread_attr_destroy删除一个线程的属性结构

2.3.1 创建线程函数pthread_create

主线程可以使用库函数 pthread_create()负责创建一个新的线程, 创建出来的新线程被称为主线程的子线 程,函数声明如下:

#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);

函数参数介绍

参数描述
threadpthread_t 类型指针, 当 pthread_create()成功返回时,新创建的线程的线程 ID 会保存在参数 thread所指向的内存中,后续的线程相关函数会使用该标识来引用此线程
attrpthread_attr_t 类型指针,指向 pthread_attr_t 类型的缓冲区, pthread_attr_t 数据类型定义了线程的各种属性,如果将参数 attr 设置为 NULL, 那么表示将线程的所有属性设置为默认值,以此创建新线程。
start_routine参数 start_routine 是一个函数指针,指向一个函数, 新创建的线程从 start_routine()函数开始运行,该函数返回值类型为void *,并且该函数的参数只有一个void *,其实这个参数就是pthread_create()函数的第四个参数 arg。如果需要向 start_routine()传递的参数有一个以上,那么需要把这些参数放到一个结构体中,然后把这个结构体对象的地址作为 arg 参数传入。
arg传递给 start_routine()函数的参数。一般情况下,需要将 arg 指向一个全局或堆变量,意思就是说在线程的生命周期中,该 arg 指向的对象必须存在,否则如果线程中访问了该对象将会出现错误。 当然也可将参数 arg 设置为 NULL,表示不需要传入参数给 start_routine()函数。

2.3.2 终止线程

可以通过如下方式终止线程的运行:

1)线程的 start 函数执行 return 语句并返回指定值,返回值就是线程的退出码;

2)线程调用 pthread_exit()函数;

3)调用 pthread_cancel()取消线程;

4)如果进程中的任意线程调用 exit(),那么将会导致所有的进程立即终止

#include <pthread.h>
void pthread_exit(void *retval);

2.3.3 线程ID

进程内部的每一个线程都有唯一的便是,称为线程ID。 线程 ID 使用 pthread_t 数据类型来表示,一个线程可通过库函数 pthread_self()来获取自己的线程 ID,其函数声明如下所示:

#include <pthread.h>
pthread_t pthread_self(void);

线程 ID 在应用程序中非常有用,原因如下: 1) 不同的线程函数,利用线程 ID 来标识要操作的目标线程, 这些函数包括pthread_cancel()、 pthread_detach()、 pthread_join()、 pthread_kill()

2) 在一些应用程序中,以特定线程的线程 ID 作为动态数据结构的标签,这某些应用场合颇为有用,既可以用来标识整个数据结构的创建者或属主线程,又可以确定随后对该数据结构执行操作的具体线程。

2.3.4 连接已终止线程

函数pthread_join() 等待由pthread_t标识的线程终止,如果该线程已经终止,则其立即返回。 通过参数 thread(线程 ID) 指定需要等待的线程;

#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);

参数介绍

参数描述
thread通过参数 thread(线程 ID) 指定需要等待的线程
retval如果参数 retval 不为 NULL,则 pthread_join()将目标线程的退出状态(即目标线程通过 pthread_exit()退出时指定的返回值或者在线程 start 函数中执行 return 语句对应的返回值)复制到retval 所指向的内存区域;如果目标线程被 pthread_cancel()取消, 则将 PTHREAD_CANCELED 放在retval 中。 如果对目标线程的终止状态不感兴趣,则可将参数 retval 设置为 NULL。

pthread_join()终止线程的特点:

1)调用 pthread_join()函数将会以阻塞的形式等待指定的线程终止,如果该线程已经终止,则 pthread_join() 立刻返回。 如果多个线程同时尝试调用 pthread_join()等待指定线程的终止,那么结果将是不确定的。

2) 若线程并未分离,则必须使用 pthread_join()来等待线程终止,回收线程资源;如果线程终止后,其它线程没有调用 pthread_join()函数来回收该线程,那么该线程将变成僵尸线程,与僵尸进程的概念相类似;同样,僵尸线程除了浪费系统资源外,若僵尸线程积累过多,那么会导致应用程序无法创建新的线程。

pthread_join()执行的功能类似于针对进程的 waitpid()调用,不过二者之间存在一些显著差别: 1)线程之间关系是对等的。进程中的任意线程均可调用 pthread_join()函数来等待另一个线程的终止。譬如,如果线程 A 创建了线程 B,线程 B 再创建线程 C,那么线程 A 可以调用 pthread_join()等待线程 C 的终止,线程 C 也可以调用 pthread_join()等待线程 A 的终止;这与进程间层次关系不同,父进程如果使用 fork()创建了子进程,那么它也是唯一能够对子进程调用 wait()的进程,线程之间不存在这样的关系。

2)不能以非阻塞的方式调用 pthread_join()。对于进程,调用 waitpid()既可以实现阻塞方式等待、也可以实现非阻塞方式等待。

2.3.5 线程分离

当线程终止时,其它线程可以通过调用 pthread_join()获取其返回状态、回收线程资源,有时,程序员并不关心线程的返回状态,只是希望系统在线程终止时能够自动回收线程资源并将其移除。在这种情况下,可以调用 pthread_detach()将指定线程进行分离,也就是分离线程。其函数声明如下所示:

#include <pthread.h>
int pthread_detach(pthread_t thread);

调用pthread_detach()后:

1)一旦线程处于分离状态,不能在使用pthread_join()来获取状态,也无法返回可连接状态;

2)其他线程调用exit(),或者主线程执行return。即使已经分离的线程也会收到影响,此时不管线程处于那种状态,进程下的所有线程都会终止。

3 线程VS进程

在应用程序中,使用多线程还是多进程呢?这要根据应用的场景进行选择。现在,先来看看多线程的优缺点吧:

线程的优点

1)线程间共享数据简单

2)创建线程要远快于进程

线程的缺点

1)多线程编程,要确保以安全的方式调用函数,多进程无需考虑这个

2)某个线程的bug可能会影响其他线程,因为它们共享相同的地址空间和其他属性

3)处在同一个进程之下的所有线程都在竞争宿主下有限的地址空间;

4)多线程中处理信号,需要特别小心;

5)在多线程程序中,所有线程运行在同一个程序下。多进程可以运行在不同的程序下。

4 线程使用的案例

4.1 使用pthread_exit退出线程

4.1.1 功能介绍

创建了两个线程,其中第一个线程是在程序运行到中途时调用 pthread_exit函数退出,第二个线程正常运行退出。在主线程中收集这两个线程的退出信息,并释放资源。

4.1.2 编写代码

创建test_thread.c文件,编写如下代码

/***************************************************************
Copyright  2024-2029. All rights reserved.
文件名     :  test_thread.c
作者       : tangmingfei2013@126.com
版本       : V1.0
描述       : pthread API test
其他       : 无
日志       : 初版V1.0 2024/03/04
​
***************************************************************/
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/types.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <linux/fs.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <assert.h>
#include <string.h>
#include <time.h>
#include <pthread.h>
​
/* 线程 -1 */
void thread1(void)
{
    int i = 0;
    
    for( i=0; i < 6; i++){
        printf("This is pthread1.\n");
        if( i == 2 ){
            pthread_exit(0);
        }
        sleep(1);
    }
}
​
/* 线程 -2 */
void thread2(void)
{
    int i = 0;
    
    for( i = 0; i < 3; i++ ){
        printf("This is pthread2.\n");
    }
    pthread_exit(0);
}
​
​
int main(void)
{
    pthread_t id1,id2;
    int ret;
    
    /* 创建线程 -1 */
    ret = pthread_create(&id1,NULL,(void *) thread1,NULL);
    if(ret!=0){
        printf ("Create pthread error!\n");
        exit (1);
    }
    
    /* 创建线程 -2 */
    ret = pthread_create(&id2,NULL,(void *) thread2,NULL);
    if(ret!=0){
        printf ("Create pthread error!\n");
        exit (1);
    }
    
    /*等待线程结束*/
    pthread_join(id1,NULL);
    pthread_join(id2,NULL);
    
    exit (0);
}
Makefile 文件:
CFLAGS= -Wall -lpthread -O2
CC=/home/ctools/gcc-linaro-4.9.4-arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc
STRIP=/home/ctools/gcc-linaro-4.9.4-arm-linux-gnueabihf/bin/arm-linux-gnueabihf-strip
​
test_thread: test_thread.o
    $(CC) $(CFLAGS) -o test_thread test_thread.o 
    $(STRIP) -s test_thread
​
clean:
    rm -f test_thread test_thread.o

4.1.3 验证

编译代码,让后在板卡上运行,得到运行结果:

4.2 修改线程属性

4.2.1 功能介绍

第一个线程设置为分离属性,并将第二个线程设置为始终运行状态,这样就可以在第二个线程运行过程中查看内存值的变化。

4.2.2 编写代码

创建test_thread.c,然后编写如下代码

/***************************************************************
Copyright  2024-2029. All rights reserved.
文件名     :  test_thread.c
作者       : tangmingfei2013@126.com
版本       : V1.0
描述       : pthread API test
其他       : 无
日志       : 初版V1.0 2024/03/04
​
***************************************************************/
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/types.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <linux/fs.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <assert.h>
#include <string.h>
#include <time.h>
#include <pthread.h>
​
/* 线程 -1 */
void thread1(void)
{
    int i = 0;
    
    for( i=0; i < 6; i++){
        printf("This is pthread1.\n");
        if( i == 2 ){
            pthread_exit(0);
        }
        sleep(1);
    }
}
​
/* 线程 -2 */
void thread2(void)
{
    int i = 0;
    
    for( i = 0; i < 3; i++ ){
        printf("This is pthread2.\n");
    }
    pthread_exit(0);
}
​
​
​
int main(void)
{
    pthread_t id1,id2;
    int ret;
    pthread_attr_t attr;
    
    /*初始化线程*/
    pthread_attr_init(&attr);
    
    /*设置线程绑定属性*/
    pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
    
    /*设置线程分离属性*/
    pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
    
    /*创建线程*/
    ret=pthread_create(&id1,&attr,(void *) thread1,NULL);
    
    if(ret!=0){
        printf ("Create pthread error!\n");
        exit (1);
    }
    
    ret=pthread_create(&id2,NULL,(void *) thread2,NULL);
    
    if(ret!=0){
        printf ("Create pthread error!\n");
        exit (1);
    }
   
    pthread_join(id2,NULL);
    return (0);
}

4.2.3 验证

编译代码,让后在板卡上运行,得到运行结果:

通过比较运行前后的内存,可知在线程1执行完成后,会立即是否内存

5 参考文献

  1. 《现代操作系统》

  2. 《linux/unix系统编程手册》

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

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

相关文章

电子电器架构 —— 车载网关路由表和刷写场景

电子电器架构 —— 车载网关路由表和刷写场景 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 PS:小细节,本文字数5000+,详细描述了网关在车载框架中的具体性能设置。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 没有人关注你。也无需有…

leetcode 热题 100_搜索二维矩阵

题解一&#xff1a; 二叉搜索树&#xff1a;从矩阵右上角观察&#xff0c;结构类似二叉搜索树&#xff0c;因此可以用类似的解法来做。具体做法是双指针从右上角开始&#xff0c;向左下角逐步搜索&#xff0c;如果当前值比目标值大&#xff0c;则向下移动&#xff0c;如果当前值…

MQ高可用相关设置

文章目录 前言MQ如何保证消息不丢失RabbitMQRocketMQKafkaMQ MQ如何保证顺序消息RabbitMQRocketMQKafka MQ刷盘机制/集群同步RabbitMQRocketMQKafka 广播消息&集群消息RabbitMQRocketMQ MQ集群架构RabbitMQRocketMQKafka 消息重试RabbitMQRockeMqKafka 死信队列RocketMQKaf…

Linux网络套接字之TCP网络程序

(&#xff61;&#xff65;∀&#xff65;)&#xff89;&#xff9e;嗨&#xff01;你好这里是ky233的主页&#xff1a;这里是ky233的主页&#xff0c;欢迎光临~https://blog.csdn.net/ky233?typeblog 点个关注不迷路⌯▾⌯ 目录 一、接口介绍 1.socket 2.listen 3.accept…

conda 设置国内源 windows+linux

默认的conda源连接不好&#xff0c;时好时坏&#xff0c;而且速度很慢&#xff0c;可以使用国内的源 如果没有安装conda&#xff0c;可以参考&#xff1a; miniconda安装&#xff1a;链接 anaconda安装winlinux&#xff1a;链接 windows使用命令提示符&#xff0c;linux使用…

后端八股笔记------Redis

Redis八股 上两种都有可能导致脏数据 所以使用两次删除缓存的技术&#xff0c;延时是因为数据库有主从问题需要更新&#xff0c;无法达到完全的强一致性&#xff0c;只能达到控制一致性。 一般放入缓存中的数据都是读多写少的数据 业务逻辑代码&#x1f447; 写锁&#x1f4…

Linux网络基础2之https

(&#xff61;&#xff65;∀&#xff65;)&#xff89;&#xff9e;嗨&#xff01;你好这里是ky233的主页&#xff1a;这里是ky233的主页&#xff0c;欢迎光临~https://blog.csdn.net/ky233?typeblog 点个关注不迷路⌯▾⌯ http是明文的可以通过一些的工具获取到正文层&#…

【Spring Boot系列】快速上手 Spring Boot

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

【数学建模】传染病模型笔记

传染病的基本数学模型&#xff0c;研究传染病的传播速度、空间范围、传播途径、动力学机理等问题&#xff0c;以指导对传染病的有效地预防和控制。常见的传染病模型按照传染病类型分为 SI、SIR、SIRS、SEIR 模型等&#xff0c;按照传播机理又分为基于常微分方程、偏微分方程、网…

《计算机网络》考研:2024/3/7 2.1.4 奈氏准则和香农定理

2024/3/7 (作者转行去干LLMs了&#xff0c;但是又想搞定考研&#xff0c;忙不过来了就全截图了呜呜呜。。。 生活真不容易。) 2.1.4 奈氏准则与香农定理

出现“error: failed to push some refs to ‘https://github.com/****.git‘”,如何解决问题

一、出错情况&#xff1a; 今天继续推送整理的知识点的时候&#xff0c;出现了一个报错。“error: failed to push some refs to https://github.com/.git”&#xff0c;百思不得其解&#xff0c;之前推送的时候都可以轻松推送成功&#xff0c;如今却说本地库与远程库不一致。…

STM32电源及时钟介绍

一、STM32最小系统 二、电源电路 2.1供电电压VDD&#xff0c;VSS F103VET6 的引角图 在 F103VET6 的引角图中可找到 49\50 角&#xff0c; 74\75 角&#xff0c; 99\100 角&#xff0c; 27\28角&#xff0c;10 \11角一共 5 对的VDD&#xff0c;VSS&#xff0c;也就是给我们芯片…

体系班第十三节

1判断完全二叉树递归做法 有四种情况&#xff1a;1 左树完全&#xff0c;右数满&#xff0c;且左高为右高加一 2左满 &#xff0c;右满&#xff0c;左高为右高加一 3左满&#xff0c;右完全&#xff0c;左右高相等 4左右均满且高相等 #include<iostream> #include&l…

外边距折叠的原因和解决

参考文章 什么时候出现外边距塌陷 外边距塌陷&#xff0c;也叫外边距折叠&#xff0c;在普通文档流中&#xff0c;在垂直方向上的2个或多个相邻的块级元素&#xff08;父子或者兄弟&#xff09;外边距合并成一个外边距的现象&#xff0c;不过只有上下外边距才会有塌陷&#x…

Guiding Large Language Models viaDirectional Stimulus Prompting

1. 通过定向刺激提示指导大语言模型 论文地址&#xff1a;[2302.11520] Guiding Large Language Models via Directional Stimulus Prompting (arxiv.org) 源码地址&#xff1a;GitHub - Leezekun/Directional-Stimulus-Prompting: [NeurIPS 2023] Codebase for the paper: &qu…

[mmucache]-ARMV8-aarch64的虚拟内存(mmutlbcache)介绍-概念扫盲

&#x1f525;博客主页&#xff1a; 小羊失眠啦. &#x1f3a5;系列专栏&#xff1a;《C语言》 《数据结构》 《C》 《Linux》 《Cpolar》 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 思考: 1、cache的entry里都是有什么&#xff1f; 2、TLB的entry里都是有什么? 3、MMU操作…

保持长期高效的七个法则(一)7 Rules for Staying Productive Long-Term(1)

Easily the best habit I’ve ever started was to use a productivity system.The idea is simple:organizing all the stuff you need to do (and how you’re going to do it) prevents a lot of internal struggle to get things done. 无疑&#xff0c;我曾经建立过的最好…

简单了解TCP/IP四层模型

什么是计算机网络&#xff1f; 计算机网络我们可以理解为一个巨大的城市地图&#xff0c;我们想从A地前往B地&#xff0c;其中要走的路、要避开的问题都交给计算机网络解决&#xff0c;直到我们可以正常的到达目的地&#xff0c;那么我们会把其中的过程抽象成一个网络模型&…

分布式执行引擎ray入门--(3)Ray Train

Ray Train中包含4个部分 Training function: 包含训练模型逻辑的函数 Worker: 用来跑训练的 Scaling configuration: 配置 Trainer: 协调以上三个部分 Ray TrainPyTorch 这一块比较建议直接去官网看diff&#xff0c;官网色块标注的比较清晰&#xff0c;非常直观。 impor…

APP2:android studio如何使用lombok

一、前言 不知道从哪个版本开始&#xff0c;android studio便无法在plugins中下载lombok了&#xff0c;有人说是内置了&#xff0c;好像有这么回事儿。我主要面临如下两个问题&#xff1a; 使用内置lombok&#xff0c;可以自动生成setter、setter、toString等。但是&#xff0…