Posix多线程编程总结

Posix在线文档:

The Single UNIX Specification, Version 2 (opengroup.org)

本文主要参考这位大神的文章:

Posix多线程编程学习笔记 - 凌峰布衣 - 博客园 (cnblogs.com)

线程安全问题

多线程编程中,经常遇到的就是线程安全问题,或者说要考虑线程同步的问题。

线程同步这个概念其实挺抽象的。关于线程同步,是指各线程执行时需要按照一定的先后顺序,而不是各线程自己想干嘛就干嘛。

本人之前对线程同步,以及主要的同步技术如互斥锁、信号量、条件变量等没怎么搞透彻,这里再想一想看看。

考虑以下三个场景:

  • 场景1:多个线程,改变了同一个全局变量;
  • 场景2:多个线程,都调用printf来打印输出;
  • 场景3:多个线程,其中A线程必须等B线程执行结束才能执行。

针对场景1,可以使用互斥锁,保证同一时间只有一个线程来操作全局变量,本质是什么呢?本质是因为修改变量的过程并不是原子操作,而是会被打断的,所以,需要在修改变量期间,保证不会被其他线程打断;

针对场景2,我们需要的其实也是printf这个过程不会被打断,要不然就是这个线程打印一会儿,那个线程打印一会儿,输出的内容都串在一起了,这种情况下怎么处理呢?在freertos中,不想某段操作被其他任务打断,可以使用临界区函数来实现,那么Linux中呢?其实可以用信号量,想一想,信号量其实主要就是统计某个资源的使用情况,printf其实就是一个输出的资源,我们可以用信号量来实现,每次只有一个线程可以获取该资源;

针对这两个场景再想想,printf是个资源,其实一个全局变量也是个资源,是不是也可以用信号量呢?另外,printf要想不被打断,是不是也可以使用互斥锁呢?理论上都是可以互换的。

二者本质上都是为了实现原子操作。

再来看看第三个场景,各线程有先后执行顺序,就可以使用条件变量,这一点,相对前面两个场景,没那么容易搞混。

而这三个技术,互斥锁、信号量以及条件变量,就是多线程编程中,实现线程同步的主要手段。再看看线程同步这个词,意思其实就是我这个线程操作资源时,你其他线程得等着,这就是同步的含义。使用线程同步技术,从而保证线程安全。

这几个技术的本质应该是类似的,只是侧重点不一样,需要在实际开发中慢慢体会。

互斥锁和信号量的区别?

参考:

信号量和互斥量(锁)的区别_信号量和互斥锁的区别-CSDN博客

为了更好地区分应用场景,再想想。

互斥锁主要是用在某个会被多线程调用的代码段中;

而信号量,是先有个信号量,然后这个线程释放信号量,另一个线程获取信号量,其实也可以控制线程实现的先后顺序。 

根据《UNIX环境高级编程》,Pthread处理线程同步的,主要是互斥锁和条件变量,而信号量在进程间通信才被提及,貌似信号量主要是用于多进程对共享数据对象的访问。 

注意Linux中的相关概念和RTOS中的相关概念的区别。

Linux中会有多个进程,相对复杂,RTOS中一般只有一个进程,也就是我们的应用,然后里面有各个子任务,对应的是Linux中的线程的概念。

有哪些不一样呢?比如RTOS中消息队列就是在各线程任务之间传递消息,而Linux中主要是用在进程间通信;又比如RTOS中的信号量分为好几种,比如二值信号量、计数信号量、互斥信号量等等,都用在任务间的同步,而Linux中,这些技术有的是用于线程的,有的是用于进程的;再比如RTOS中有临界区保护这一技术手段,不过Linux中一般就是通过互斥锁信号量等等实现。

总之要明确的是,RTOS中一般只有线程的概念,也就是各子任务,所以各技术,都是用在任务之间;但是Linux中,既有线程又有进程,不同的技术有不同的应用场景,所以要加以区分。 另外,不同的操作系统,可能略有差别,实际使用时,需要进一步了解,不过用法都大差不差,重要的是,别以为都是统一的用法,然后产生疑惑,还不知道是咋回事。

Pthreads 概述

历史上,硬件销售商实现了私有版本的多线程库。这些实现在本质上各自不同,使得程序员难于开发可移植的应用程序。 为了使用线程所提供的强大优点,需要一个标准的程序接口。对于UNIX系统,IEEE POSIX 1003.1c(1995)标准制订了这一标准接口。依赖于该标准的实现就称为POSIX threads 或者Pthreads。现在多数硬件销售商也提供Pthreads,附加于私有的API。 Pthreads 被定义为一些C语言类型和函数调用,用pthread.h头(包含)文件和线程库实现。

所以说,Pthreads,其实就是POSIX threads,也就是符合posix标准的线程操作接口。

线程管理

Pthreads API中的函数可以非正式的划分为三大类: 

线程管理(Thread management: 第一类函数直接用于线程:创建(creating),分离(detaching),连接(joining)等等。包含了用于设置和查询线程属性(可连接,调度属性等)的函数。 

互斥量(Mutexes: 第二类函数是用于线程同步的,称为互斥量(mutexes),是"mutual exclusion"的缩写。Mutex函数提供了创建,销毁,锁定和解锁互斥量的功能。同时还包括了一些用于设定或修改互斥量属性的函数。 

条件变量(Condition variables):第三类函数处理共享一个互斥量的线程间的通信,基于程序员指定的条件。这类函数包括指定的条件变量的创建,销毁,等待和受信(signal)。设置查询条件变量属性的函数也包含其中。 

创建和结束线程

函数: 

pthread_create (thread,attr,start_routine,arg)  

pthread_exit (status)  

pthread_attr_init (attr)  

pthread_attr_destroy (attr)  

最初,main函数包含了一个缺省的线程,也就是主线程。其它线程则需要程序员显式地创建。 

有个疑惑,主线程mian里面创建完其他线程后,是进入while死循环,还是直接return返回?

参考:

线程创建后,避免主线程先于子线程结束的四种方式_怎么让新创建的线程不随主线程结束运行-CSDN博客

pthread_create 创建一个新线程并使之运行起来。该函数可以在程序的任何地方调用。 

#include <pthread.h>

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

pthread_create参数: 

  • thread:返回一个新线程标识符,也就是线程ID
  • attr:线程属性对象。可以指定一个线程属性对象,或者NULL为缺省值,一般需要时会在属性里设置线程的优先级和栈大小;
  • start_routine:线程将会执行的函数,注意该函数的类型void *(*)(void *)
  • arg: 传递给start_routine单个参数,传递时必须转换成指向void的指针类型。没有参数传递时,可设置为NULL。 

一个进程可以创建的线程最大数量取决于系统实现。 

Q:一个线程被创建后,怎么知道操作系统何时调度该线程使之运行? 

A:线程何时何地被执行取决于操作系统实现,强壮的程序应该不依赖于线程执行的顺序。

线程属性: 

  • 线程被创建时会带有默认的属性。其中的一些属性可以被程序员用线程属性对象来修改;
  • pthread_attr_init 和 pthread_attr_destroy用于初始化/销毁先成属性对象; 

结束终止: 

  • 结束线程的方法有以下几种: 
    • 线程从主线程(main函数的初始线程)返回; 
    • 线程调用了pthread_exit函数; 
    • 其它线程使用 pthread_cancel函数结束线程; 
    • 调用exec或者exit函数,整个进程结束; 

pthread_exit用于显式退出线程。典型地,pthread_exit()函数在线程完成工作不再需要的时候被调用,退出线程。注意,哪个线程调用的,就结束哪个线程。

#include <pthread.h>

void pthread_exit(void *value_ptr);

参数retval 是 void* 类型的指针,可以指向任何类型的数据,它指向的数据将作为线程退出时的返回值。如果线程不需要返回任何数据,将 retval 参数置为 NULL 即可。

示例如下:

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

void *thread_fun1(void *param)
{
    while(1)
    {
        printf("I am thread-1\n");
        sleep(1);
    }
    
    return NULL;
}

void *thread_fun2(void *param)
{
    while(1)
    {
        printf("you are thread-2\n");
        sleep(1);
    }
    
    return NULL;
}

int main(int argc, char *argv[])
{
    pthread_t tid1, tid2;
    int rc;

    rc = pthread_create(&tid1, NULL, thread_fun1, NULL);
    if (rc < 0){ 
         printf("ERROR; return code from pthread_create() is %d\n", rc); 
         exit(-1); 
    } 

    rc = pthread_create(&tid2, NULL, thread_fun2, NULL);
    if (rc < 0){ 
         printf("ERROR; return code from pthread_create() is %d\n", rc); 
         exit(-1); 
    }

    pthread_exit(NULL);//主线程退出了
}

互斥量

 

互斥量(Mutex)是“mutual exclusion”的缩写。互斥量是实现线程同步,和保护同时写共享数据的主要方法。

互斥量对共享数据的保护就像一把锁。在Pthreads中,任何时候仅有一个线程可以锁定互斥量,因此,当多个线程尝试去锁定该互斥量时仅有一个会成功。直到锁定互斥量的线程解锁互斥量后,其他线程才可以去锁定互斥量。线程必须轮着访问受保护数据。 

互斥量可以防止“竞争”条件。

一个拥有互斥量的线程经常用于更新全局变量。确保了多个线程更新同样的变量以安全的方式运行,最终的结果和一个线程处理的结果是相同的(在受保护的地方,多个线程对于资源的操作,就像是一个线程中对数据的先后处理)。这个更新的变量属于一个“临界区(critical section)”。 

使用互斥量的典型顺序如下: 

  • 创建和初始一个互斥量 
  • 多个线程尝试去锁定该互斥量 
  • 仅有一个线程可以成功锁定改互斥量 
  • 锁定成功的线程做一些处理 
  • 线程解锁该互斥量 
  • 另外一个线程获得互斥量,重复上述过程 
  • 最后销毁互斥量 

当多个线程竞争同一个互斥量时,失败的线程会阻塞在lock调用处。可以用“trylock”替换“lock”,则失败时不会阻塞。 

当保护共享数据时,程序员有责任去确认是否需要使用互斥量。如,若四个线程会更新同样的数据,但仅有一个线程用了互斥量,则数据可能会损坏。 

如下程序:

补充

编译时报错:

undefined reference to 'pthread_create'

 

参考:

Linux下undefined reference to ‘pthread_create’问题解决-CSDN博客

问题原因:
pthread 库不是 Linux 系统默认的库,连接时需要使用静态库 libpthread.a,所以在使用pthread_create()创建线程,以及调用 pthread_atfork()函数建立fork处理程序时,需要链接该库。
问题解决:
在编译中要加 -lpthread参数
gcc thread.c -o thread -lpthread
thread.c为你些的源文件,不要忘了加上头文件#include<pthread.h>

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

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

相关文章

图片覆盖攻击

点击劫持的本质是一种视觉欺骗。顺着这个思路&#xff0c;还有一些攻击方法也可以起到类似的作 用&#xff0c;比如图片覆盖。 一名叫 sven.vetsch 的安全研究者最先提出了这种 Cross Site Image Overlaying 攻击&#xff0c;简称 XSIO。sven.vetsch 通过调整图片的 style 使得…

CDP问卷的目的是什么?CDP问卷对企业有什么好处

CDP问卷&#xff08;Carbon Disclosure Project问卷&#xff09;的目的是多方面的&#xff0c;主要包括以下几点&#xff1a; 推动透明度&#xff1a;CDP问卷要求企业公开披露其温室气体排放数据、减排目标和行动&#xff0c;以及气候风险管理策略等信息。这有助于增强企业的透…

Linux进程概念(二)

上期我们已经学习了进程的基础的内容&#xff0c;已经对进程的基本概念有了了解&#xff0c;知道了进程的组成&#xff0c; 本期我们将以操作为主进一步探讨进程的相关概念。 目录 查看进程 创建进程 查看进程 查看进程主要有两种方式。 ps ajx指令 在当前目录下有名为tes…

Canvas绘制图片和区域

如何使用Canvas在图片上绘制区域&#xff1f; 一. 首先&#xff0c;我们需要初始化三个canvas画布&#xff08;初始化Canvas&#xff09; initCanvas() {// 初始化canvas画布let canvasWrap document.getElementsByClassName("canvas-wrap");this.wrapWidth canva…

更改ip后还被封是ip质量的原因吗?

不同的代理IP的质量相同&#xff0c;一般来说可以根据以下几个因素来进行判断&#xff1a; 1.可用率 可用率就是提取的这些代理IP中可以正常使用的比率。假如我们无法使用某个代理IP请求目标网站或者请求超时&#xff0c;那么就代表这个代理不可用&#xff0c;一般来说免费代…

ubuntu18.04 编译HBA 并实例运行

HBA是一个激光点云层级式的全局优化的程序&#xff0c;他的论文题目是&#xff1a;HBA: A Globally Consistent and Efficient Large-Scale LiDAR Mapping Module&#xff0c;对应的github地址是&#xff1a;HKU-Mars-Lab GitHub 学习本博客&#xff0c;可以学到gtsam安装&am…

Vienna 整流器的基本原理及数学模型

2.1 Vienna 整流器基本工作原理 2.1.1 主电路拓扑结构分析 Vienna 整流器系统的主电路包含用于升压的三相电感、三相桥臂和两个直流侧均压电容。通过有规律的对双向开关进行控制不仅能实现功率双向流动&#xff0c;还能使网侧电流时刻跟踪电网电压&#xff0c;使系统运行在高…

昇思25天学习打卡营第5天|网络构建

一、简介&#xff1a; 神经网络模型是由神经网络层和Tensor操作构成的&#xff0c;mindspore.nn提供了常见神经网络层的实现&#xff0c;在MindSpore中&#xff0c;Cell类是构建所有网络的基类&#xff08;这个类和pytorch中的modul类是一样的作用&#xff09;&#xff0c;也是…

陀螺仪LSM6DSV16X与AI集成(7)----FIFO数据读取与配置

陀螺仪LSM6DSV16X与AI集成.7--检测自由落体 概述视频教学样品申请源码下载主要内容生成STM32CUBEMX串口配置IIC配置CS和SA0设置串口重定向参考程序初始换管脚获取ID复位操作BDU设置设置量程设置FIFO水印设置速率使用流模式设置FIFO时间戳批处理速率使能时间戳FIFO状态寄存器演示…

Mathtype7在Word2016中闪退(安装过6)

安装教程&#xff1a;https://blog.csdn.net/Little_pudding10/article/details/135465291 Mathtype7在Word2016中闪退是因为安装过Mathtype6&#xff0c;MathPage.wll和MathType Comm***.dotm)&#xff0c;不会随着Mathtype的删除自动删除&#xff0c;而新版的Mathtype中的文件…

【从0实现React18】 (三) 初探reconciler 带你初步探寻React的核心逻辑

Reconciler 使React核心逻辑所在的模块&#xff0c;中文名叫协调器&#xff0c;协调(reconciler)就是diff算法的意思 reconciler有什么用&#xff1f; 在前端框架出现之前&#xff0c;通常会使用 jQuery 这样的库来开发页面。jQuery 是一个过程驱动的库&#xff0c;开发者需要…

Django 模版过滤器

Django模版过滤器是一个非常有用的功能&#xff0c;它允许我们在模版中处理数据。过滤器看起来像这样&#xff1a;{{ name|lower }}&#xff0c;这将把变量name的值转换为小写。 1&#xff0c;创建应用 python manage.py startapp app5 2&#xff0c;注册应用 Test/Test/sett…

【git1】指令,commit,免密

文章目录 1.常用指令&#xff1a;git branch查看本地分支&#xff0c; -r查看远程分支&#xff0c; -a查看本地和远程&#xff0c;-v查看各分支最后一次提交, -D删除分支2.commit规范&#xff1a;git commit进入vi界面&#xff08;进入前要git config core.editor vim设一下vi模…

Java项目:基于SSM框架实现的精品酒销售管理系统分前后台【ssm+B/S架构+源码+数据库+毕业论文】

一、项目简介 本项目是一套基于SSM框架实现的精品酒销售管理系统 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&#xff0c;eclipse或者idea 确保可以运行&#xff01; 该系统功能完善、界面美观、操作简单、功…

【break】大头哥哥做题

【break】大头哥哥做题 时间限制: 1000 ms 内存限制: 65536 KB 【题目描述】 【参考代码】 #include <iostream> using namespace std; int main(){ int sum 0;//求和int day 0;//天数 while(1){int a;cin>>a;if(a-1){break;//结束当前循环 }sum sum a; …

121.网络游戏逆向分析与漏洞攻防-邮件系统数据分析-邮件读取与发送界面设计

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 如果看不懂、不知道现在做的什么&#xff0c;那就跟着做完看效果 现在的代码都是依据数据包来写的&#xff0c;如果看不懂代码&#xff0c;就说明没看懂数据包…

Nginx+Lua+Redis 实现Nginx301跳转配置管理

业务场景需求 long long ago&#xff1a; 在项目的运维过程中有一次SEO团队提出 网页的URL 中如果可以带上关键字&#xff0c;那么网页在各大搜索引擎中收录和排名有非常重大的突出优势&#xff08;~~SEO团队到底专不专业 ~~&#xff0c;此处不做置评&#xff09;&#xff0c;…

C/C++ strftime函数

目录 strftime()函数 函数原型 头文件 功能 返回值 参数 案例 结语 strftime()函数 函数原型 size_t strftime(char *s, size_t max, const char *format, const struct tm *tm); 头文件 #include <time.h> 功能 用于日期和时间格式化的函数&#xff0c;它允许你…

【算法】二叉树 - 理论基础

1.种类 1.1 满二叉树 只有度为0和2的节点&#xff0c;且度为0的节点都都在同一层。深度为k&#xff0c;有2^k-1个节点。 1.2 完全二叉树 在完全二叉树中&#xff0c;除了最底层节点可能没填满外&#xff0c;其余每层节点数都达到最大值&#xff0c;并且最下面一层的节点都…

【论文复现|智能算法改进】一种基于多策略改进的鲸鱼算法

目录 1.算法原理2.改进点3.结果展示4.参考文献5.代码获取 1.算法原理 SCI二区|鲸鱼优化算法&#xff08;WOA&#xff09;原理及实现【附完整Matlab代码】 2.改进点 混沌反向学习策略 将混沌映射和反向学习策略结合&#xff0c;形成混沌反向学习方法&#xff0c;通过该方 法…