深入理解多线程

news2024/12/24 0:25:54

一、线程基本概念

1、概述

线程是允许应用程序并发的一种机制。线程共享进程内的所有资源。

线程是调度的基本单位。

每个线程都有自己的 errno。

所有 pthread 函数均以返回 0 表示成功,返回一个正值表示失败。

编译 pthread 程序需要添加链接库(-lpthread)。

线程的主要优势在于,能够通过全局变量来共享信息。同时也引入一个问题,多个线程对临界资源的竞争。

2、线程终止方式

1、线程函数执行 return 语句并返回指定值。

2、线程调用 pthread_exit。

3、调用 pthread_cancel() 取消线程。

4、任意线程调用 exit(), 或者主线程执行 return 语句。

二、Pthreads 数据类型

在这里插入图片描述

三、线程接口

1、创建线程

#include <pthread.h>

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

pthread_create() 创建线程通过调用带有 arg 参数的 start_routine 函数开始执行。

2、终止线程

#include <pthread.h>

void pthread_exit(void *retval);

3、线程 ID

#include <pthread.h>

// 获取当前线程 ID
pthread_t pthread_self(void);

// 判断 2 个线程ID是否相等
int pthread_equal(pthread_t t1, pthread_t t2);

4、连接已终止的线程

#include <pthread.h>

int pthread_join(pthread_t thread, void **retval);

pthread_join 等待由 thread 标识的线程终止。

如果 pthread_join 传入一个之前已经连接过的线程 ID,将导致无法预知的行为。

默认情况下,线程是可连接的(join),也就是程序退出时,其他线程可以通过调用 pthread_join() 获取其返回状态。

5、线程分离

#include <pthread.h>

int pthread_detach(pthread_t thread);

有时,我们不关心程序的返回状态,只是希望系统在线程终止时能够自动清理并移除。在这种情况下,可以调用 pthread_detach 并向 thread 参数传入指定线程的标识符,将该线程标记为处于分离状态。

线程可以通过调用 pthread_detach() 实现自行分离。

一旦线程处于分离状态,就不能再使用 pthread_join() 获取其状态,也无法使其重返可连接状态。

6、线程取消

#include <pthread.h>

int pthread_cancel(pthread_t thread);

7、线程可取消性检查

#include <pthread.h>

void pthread_testcancel(void);

8、清理函数

#include <pthread.h>

void pthread_cleanup_push(void (*routine)(void *),void *arg);
void pthread_cleanup_pop(int execute);

9、向线程发送信号

#include <signal.h>

int pthread_kill(pthread_t thread, int sig);
#include <signal.h>
#include <pthread.h>

int pthread_sigqueue(pthread_t thread, int sig,const union sigval value);

10、操作线程信号掩码

#include <signal.h>

int pthread_sigmask(int how, const sigset_t *set, sigset_t *oldset);

四、线程属性

五、线程同步 - 保护共享变量的访问:互斥量

1、分配互斥量

1、静态分配

pthread_mutex_t mutex;

2、动态分配

#include <pthread.h>

int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

2、销毁互斥量

#include <pthread.h>

int pthread_mutex_destroy(pthread_mutex_t *mutex);

3、加锁和解锁互斥量

#include <pthread.h>

int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);

调用 pthread_mutex_lock() 锁定互斥量。如果互斥量当前处于未锁定状态,该调用将锁定互斥量并立即返回。如果互斥量处于锁定状态,pthread_mutex_lock() 调用会一直阻塞,直到该互斥量被解锁。

pthread_mutex_unlock() 解锁调用线程锁定的互斥量,以下行为均为错误:

1、对未锁定的互斥量进行解锁。

2、解锁其他线程锁定的互斥量。

4、互斥量死锁

5、互斥量属性

6、互斥量类型

1、PTHREAD_MUTEX_NORMAL

不具备死锁自检功能。

线程试图对已由自己锁定的互斥量加锁,则发生死锁。

互斥量处于未锁定状态,或由其他线程锁定,对其解锁会导致不确定的结果。

2、PTHREAD_MUTEX_ERRORCHECK

此类互斥量的所有操作都会执行错误检查。

可以作为调试工具,以发现程序在哪里违反了互斥量使用的基本原则。

3、PTHREAD_MUTEX_RECURSIVE

该互斥量维护一个锁计数器。当线程第一次取得互斥量时,会将锁定计数器置1,后续同一线程的每次加锁操作会递增锁定计数器的数值,而解锁操作则会递减计数器计数,只有锁计数器值降至0时,才会释放。

六、线程同步 - 通知状态的改变:条件变量

条件变量允许一个线程就某个共享变量(或其他共享资源)的状态变化通知其他线程。

条件变量总是结合互斥量使用。

所有线程都应该处理虚假的唤醒

条件变量并不保存状态信息,只是传递应用程序状态信息的一种通讯机制。发送信号时,若无任何线程在等待条件变量,这个信号也就不了了之。

1、分配条件变量

1、静态分配

pthread_cond_t cond;

2、动态分配

#include <pthread.h>

int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

2、销毁条件变量

#include <pthread.h>

int pthread_cond_destroy(pthread_cond_t *cond);

3、通知条件变量

#include <pthread.h>

// 至少唤起一个线程
int pthread_cond_signal(pthread_cond_t *cond);
// 唤起所有阻塞线程
int pthread_cond_broadcast(pthread_cond_t *cond);

4、等待条件变量

#include <pthread.h>

int pthread_cond_timedwait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex,const struct timespec *restrict abstime);

int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);

七、线程安全

1、可重入性

要诀:避免使用全局变量和静态变量。

2、一次性初始化

#include <pthread.h>

int pthread_once(pthread_once_t *once_control,void (*init_routine)(void));
pthread_once_t once_control = PTHREAD_ONCE_INIT;

3、线程特有数据

4、线程局部存储

附录一:多线程示例

1、main.c

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

void* pthread_message_function(void *ptr);

int main(int argc, char *argv[])
{
    pthread_t pthreadID1,pthreadID2;
    int ret = 0;
    char *message1 = "thread1";
    char *message2 = "thread2";

    /* 1、创建线程1 */
    ret = pthread_create(&pthreadID1,NULL,pthread_message_function,(void*)message1);
    if(ret != 0){
        printf("%s create fail!\r\n",message1);
    }else{
        printf("%s create sucess!\r\n",message1);
    }
    /* 2、创建线程2 */
    ret = pthread_create(&pthreadID2,NULL,pthread_message_function,(void*)message2);
    if(ret != 0){
        printf("%s create fail!\r\n",message2);
    }else{
        printf("%s create sucess!\r\n",message2);
    }

    /* 3、休眠一定时间,等待子线程结束 */
    sleep(10);
    printf("main thread exit\r\n");
    return 0;
}

void* pthread_message_function(void *ptr)
{
    int i = 0;
    /* 分离线程 - 避免僵尸线程产生 */
    pthread_detach(pthread_self());
    /* 业务逻辑 */
    for (i; i<5; i++) {
        printf("%s thread: %d\n", (char *)ptr, i);
        sleep(1);
    }
}

2、makefile

a.out: main.c
	gcc main.c -o a.out -lpthread

.PHONY : clean 
clean:
	rm -rf a.out

3、测试

[root@localhost pthread]# make
gcc main.c -o a.out -lpthread
[root@localhost pthread]# ls
a.out  main.c  makefile
[root@localhost pthread]# ./a.out 
thread1 create sucess!
thread2 create sucess!
thread2 thread: 0
thread1 thread: 0
thread2 thread: 1
thread1 thread: 1
thread2 thread: 2
thread1 thread: 2
thread2 thread: 3
thread1 thread: 3
thread2 thread: 4
thread1 thread: 4
main thread exit
[root@localhost pthread]# 

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

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

相关文章

【Java】反射机制和代理机制

目录一、反射1. 反射概念2. 反射的应用场景3. 反射机制的优缺点4. 反射实战获取 Class 对象的四种方式二、代理机制1. 代理模式2. 静态代理3. 动态代理3.1 JDK动态代理机制1. 介绍2.JDK 动态代理类使用步骤3. 代码示例3.2 CGLIB 动态代理机制1.介绍2.CGLIB 动态代理类使用步骤3…

程序员压力大?用 PyQt 做一个美*女GIF设置桌面,每天都有好心情

嗨害大家好鸭&#xff01;我是小熊猫~ 要说程序员工作的最大压力不是来自于工作本身&#xff0c; 而是来自于需要不断学习才能更好地完成工作&#xff0c; 因为程序员工作中面对的编程语言是在不断更新的&#xff0c; 同时还要学习熟悉其他语言来提升竞争力… 好了&#xff0c…

使用Python通过拉马努金公式快速求π

使用Python通过拉马努金公式快速求π 一、前言 π是一个数学常数&#xff0c;定义为&#xff1a;圆的周长与直径的比值。 π是一个无理数&#xff0c;也是一个超越数&#xff0c;它的小数部分无限不循环。 π可以用来精确计算圆周长、圆面积、球体积等几何形状的关键值。 有关…

【电子学会】2022年12月图形化二级 -- 老鹰捉小鸡

老鹰捉小鸡 小鸡正在农场上玩耍&#xff0c;突然从远处飞来一只老鹰&#xff0c;小鸡要快速回到鸡舍中&#xff0c;躲避老鹰的抓捕。 1. 准备工作 &#xff08;1&#xff09;删除默认白色背景&#xff0c;添加背景Farm&#xff1b; &#xff08;2&#xff09;删除默认角色小…

进制间转换

md&#xff0c;离开学校好多年了&#xff0c;这些基础趁现在还记得记录一下&#xff0c;不然怕哪天还给老师就尴尬了&#xff0c;方便复习 基本概念 二进制&#xff1a;&#xff08;逢2进1&#xff09;由0和1组成。十六进制&#xff1a;&#xff08;逢16进1&#xff09;由0-9&a…

编码器SIQ-02FVS3驱动

一.简介 此编码器可以是功能非常强大&#xff0c;可以检测左右转动&#xff0c;和按键按下&#xff0c;所以说这一个编码器可以抵三个按键&#xff0c;而且体积非常小&#xff0c;使用起来比三个按键要高大尚&#xff0c;而且驱动也简单。唯一不足的点就是价格有点小贵6-8元才…

Faster RCNN 论文阅读

1.网络架构 VGG16网络 anchors:人工放上去的 RPN对anchors进行二分类&#xff0c;正样本&#xff0c;负样本 RoIP&#xff1a;前面的框框已经圈出目标&#xff0c;但还不知道具体属于哪个类&#xff0c;它就是干这个工作的 2.VGG网络 VGG网络可以任意替换其他的任意神经网络&am…

Spring核心模块——Aware接口

Aware接口前言基本内容例子结尾前言 Spring的依赖注入最大亮点是所有的Bean对Spring容器对存在都是没有意识到&#xff0c;Spring容器中的Bean的耦合度是很低的&#xff0c;我们可以将Spring容器很容易换成其他的容器。 但是实际开发的时候&#xff0c;我们经常要用到Spring容…

虚拟机安装Windows 10

虚拟机安装Windows 10 镜像下载 方法一&#xff1a;下载我制作好的镜像文件->百度网盘链接 提取码&#xff1a;Chen 方法二&#xff1a;自己做一个 进入微软官网链接 下载"MediaCreationTool20H2" 运行该工具 点击下一步选择路径&#xff0c;等他下载好就欧克了…

我就不信你还不懂HashSet/HashMap的底层原理

&#x1f4a5;注&#x1f4a5; &#x1f497;阅读本博客需备的前置知识如下&#x1f497; &#x1f31f;数据结构常识&#x1f31f;&#x1f449;1️⃣八种数据结构快速扫盲&#x1f31f;Java集合常识&#x1f31f;&#x1f449;2️⃣Java单列集合扫盲 ⭐️本博客知识点收录于…

MicroBlaze系列教程(7):AXI_SPI的使用(M25P16)

文章目录 AXI_SPI简介MicroBlaze硬件配置常用函数使用示例波形实测参考资料工程下载本文是Xilinx MicroBlaze系列教程的第7篇文章。 AXI_SPI简介 Xilinx AXI-SPI IP共有两个:一个是标准的AXI_SPI,即4线制SPI,CS、SCLK、MOSI和MISO,另一个是AXI_Quad SPI,支持配置成标准SP…

pygame10 扫雷游戏3

上一节课我们完成了扫雷游戏地图中雷数量的显示&#xff0c;今天我们将把雷的生成做出来 一、地雷的生成 地图中有20*20共400个格子&#xff0c;我们可以设定一共可以生成40个地雷&#xff0c;为了使得每次生成的地图都不一样&#xff0c;可以使用随机数randint&#xff0c;每…

为什么使用Junit单元测试?Junit的详解

Hi I’m Shendi 为什么使用Junit单元测试&#xff1f;Junit的详解 Junit简介 Junit是一个Java语言的单元测试框架。 单元测试是一个对单一实体&#xff08;类或方法&#xff09;的测试 JUnit是由 Erich Gamma 和 Kent Beck 编写的一个回归测试框架&#xff08;regression test…

AUTOSAR FunctionalSafety

概述 随着汽车功能复杂度的急剧增加,功能安全作为一个系统特征开始被重视,影响着系统设计决策。软件作为一个因素影响着系统级的复杂度。新的技术和概念可以被用在软件开发中来减少复杂度,来实现功能安全。 AUTOSAR提供了一些安全措施和机制来支持安全系统开发,但是并不是…

MATLAB绘制椭圆形相关系矩阵图

数据/代码准备 数据及代码下载&#xff1a; 下载专区-《MATLAB统计分析与应用&#xff1a;40个案例分析》程序与数据 绘图函数&#xff1a; matrixplot(data, PARAM1,val1, PARAM2,val2, ...) 案例 数据如下&#xff1a; MATLAB代码如下&#xff1a; clc close all clear …

升级 vue3 常见问题总汇

Ⅰ、前言 虽然 vue3 是没有删除 vue2 的 选项式 API &#xff0c; 但是我们升级vue3 还是需要修改很多问题的下面来看看我们升级常见的一些问题 &#x1f447; 文章目录Ⅰ、前言Ⅱ、解决兼容问题1、路由的创建方式2、路由的方法变化3、升级 vuex 到 4.x4、作用域 插槽语法修改…

Hyperf使用RabbitMQ消息队列

Hyperf连接使用RabbitMQ消息中间件 传送门 使用Docker部署RabbitMQ&#xff0c;->传送门<使用Docker部署Hyperf&#xff0c;->传送门-< 部署环境 安装amqp扩展 composer require hyperf/amqp安装command命令行扩展 composer require hyperf/command配置参数 假…

Windows+VS2019用vcpkg编译colmap以及用Cmake编译colmap源码

WindowsVS2019用vcpkg编译colmap以及用Cmake编译colmap源码 Window下官方建议用vcpkg安装。这里我已经安装好了VS2019以及cuda11.7。 1.安装vcpkg git clone https://github.com/microsoft/vcpkg cd vcpkg .\bootstrap-vcpkg.bat2. 使用vcpkg编译colmap .\vcpkg install co…

Java软件开发好学吗?学完好找工作吗?

互联网高速发展的当下&#xff0c;Java语言无处不在&#xff1a;手机APP、Java游戏、电脑应用&#xff0c;都有它的身影。作为最热门的开发语言之一&#xff0c;Java在编程圈的地位不可撼动。可是&#xff0c;听名字就很专业的样子。Java语言到底好学吗&#xff1f;刚入坑编程圈…

CAPL脚本要注意区分elcount和strlen求数组长度的区别,不然要吃大亏

&#x1f345; 我是蚂蚁小兵&#xff0c;专注于车载诊断领域&#xff0c;尤其擅长于对CANoe工具的使用&#x1f345; 寻找组织 &#xff0c;答疑解惑&#xff0c;摸鱼聊天&#xff0c;博客源码&#xff0c;点击加入&#x1f449;【相亲相爱一家人】&#x1f345; 玩转CANoe&…