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>

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

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

相关文章

PINN解偏微分方程实例4

PINN解偏微分方程实例4 一、正问题1. Diffusion equation2. Burgers’ equation3. Allen–Cahn equation4. Wave equation 二、反问题1. Burgers’ equation3. 部分代码示例 本文使用 PINN解偏微分方程实例1中展示的代码求解了以四个具体的偏微分方程&#xff0c;包括Diffusio…

第T2周:彩色图片分类

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 &#x1f449; 要求&#xff1a; 学习如何编写一个完整的深度学习程序了解分类彩色图片会灰度图片有什么区别测试集accuracy到达72% &#x1f9be;我的环境&am…

测试测量-DMM直流精度

测试测量-DMM直流精度 最近去面试&#xff0c;发现了自己许多不足&#xff0c;比如我从未考虑过万用表准或者不准&#xff0c;或者万用表有多准&#xff1f; 在过去的实验室中&#xff0c;常用的DMM有KEYSIGHT 34401A以及 KEITHLEY THD2015&#xff0c;就以这两台为例&#x…

Leetcode 3195. Find the Minimum Area to Cover All Ones I

Leetcode 3195. Find the Minimum Area to Cover All Ones I 1. 解题思路2. 代码实现 题目链接&#xff1a;3195. Find the Minimum Area to Cover All Ones I 1. 解题思路 这一题还是挺简单的&#xff0c;只要找到所有1所在的元素的上下左右4个边界&#xff0c;作为目标矩形…

线程C++

#include <thread> #include <chrono> #include <cmath> #include <mutex> #include <iostream> using namespace std;mutex mtx; void threadCommunicat() {int ans 0;while (ans<3){mtx.lock();//上锁cout << "ans" <…

预制直埋聚氨酯保温管

&#x1f600;宝子们&#xff0c;今天来给大家介绍一下预制聚氨酯直埋保温管&#x1f389;&#xff01; 它可是个好东西&#x1f44d;&#xff0c;具有超强的保温性能&#x1f9ca;&#xff0c;能够有效保持管道内的温度&#x1f60e;。 而且它还很耐用&#x1f4aa;&#xff0…

解析Java中1000个常用类:AbstractSet类,你学会了吗?

推荐一个我自己写的小报童专栏导航网站: http://xbt100.top 收录了生财有术项目精选、AI海外赚钱、纯银的产品分析等专栏,陆续会收录更多的专栏,欢迎体验~复制URL可直达。 以下是正文。 在 Java 集合框架中,AbstractSet 是一个重要的抽象类,为实现自定义的集合(Set)提…

【Python】处理 scikit-learn 中的 SettingWithCopyWarning

那年夏天我和你躲在 这一大片宁静的海 直到后来我们都还在 对这个世界充满期待 今年冬天你已经不在 我的心空出了一块 很高兴遇见你 让我终究明白 回忆比真实精彩 &#x1f3b5; 王心凌《那年夏天宁静的海》 这不是一个错误&#xff0c;而是一个 SettingW…

用 idea 启动多个实例

在学习负载均衡的时候&#xff0c;要模拟多个实例均提供一个服务&#xff0c;我们要如何用 idea 启动多个实例呢&#xff1f; 如下图&#xff0c;我们已经启动了一个 ProductService 服务&#xff0c;现在想再启动两个相同的服务 1. 选中要启动的服务,右键选择 Copy Configura…

用Java获取键盘输入数的个十百位数

这段Java代码是一个简单的程序&#xff0c;用于接收用户输入的一个三位数&#xff0c;并将其分解为个位、十位和百位数字&#xff0c;然后分别打印出来。下面是代码的详细解释&#xff1a; 导入所需类库: import java.util.Scanner;&#xff1a;导入Scanner类&#xff0c;用于从…

opencv学习笔记(3)

绘制直线 line(img, 开始点&#xff0c;结束点&#xff0c;颜色&#xff0c;线宽&#xff0c;线型(默认为8)) import cv2 import numpy as npimg np.zeros((640, 480, 3), np.uint8)#画线&#xff0c;坐标点为(x, y) cv2.line(img, (10, 20), (10, 220), (0, 0, 255), 5, 4)…

【经典算法】LeetCode 22括号生成(Java/C/Python3/Go实现含注释说明,中等)

作者主页&#xff1a; &#x1f517;进朱者赤的博客 精选专栏&#xff1a;&#x1f517;经典算法 作者简介&#xff1a;阿里非典型程序员一枚 &#xff0c;记录在大厂的打怪升级之路。 一起学习Java、大数据、数据结构算法&#xff08;公众号同名&#xff09; ❤️觉得文章还…

urllib3版本与系统openssl版本不兼容

urllib3版本与系统openssl版本不兼容 报错信息 ImportError: urllib3 v2.0 only supports OpenSSL 1.1.1, currently the ssl 解决办法 安装urllib3的1.x.xx版本&#xff0c;如&#xff1a; pip install urllib31.25.11

UsersGUI.java用户界面

完成效果图&#xff1a; 点击阅读按钮&#xff1a; 点击删除按钮&#xff1a; 点击新建按钮&#xff1a; Code /* This GUI application allows users to manage their diaries: ​ Read: Users can read existing diaries. Create: Users can create new diaries. Delete: Us…

ARC学习(3)基本编程模型认识(三)

笔者来介绍arc的编程模型的中断流程和异常流程 1、中断介绍 主要介绍一下中断进入的流程&#xff0c;包括需要配置的寄存器等信息。 中断号&#xff1a;16-255&#xff0c;总共240个中断。触发类型&#xff1a;脉冲或者电平触发中断优先级&#xff1a;16个&#xff0c;0最大&…

【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模…

DVWA-XSS(Stored)-httponly分析

拿DVWA的XSS为例子 httponly是微软对cookie做的扩展。这个主要是解决用户的cookie可能被盗用的问题。 接DVWA的分析&#xff0c;发现其实Impossible的cookie都是设置的httponly1&#xff0c;samesite1. 这两个参数的意思参考Set-Cookie HttpOnly:阻止 JavaScript 通过 Documen…

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

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

文本三剑客—sed命令

sed命令 一、概念 sed是一种流编辑器&#xff0c;一次处理一行内容。 处理方式&#xff1a;一行一行处理&#xff0c;处理完当前行&#xff0c;才会处理下一行&#xff0c;直到文件末尾。 如果只是展示&#xff0c;会放在缓冲区&#xff08;模式空间&#xff09;&#xff0…

微信公众号 H5授权登录实现(最详细)

一、微信公众号 &#xff08;一&#xff09;基础信息 微信授权类型 自己的网站、APP等第三方&#xff0c;要实现接入微信授权登录&#xff0c;有多种方式&#xff1a;微信公众号&#xff08;网页&#xff09;、微信小程序、微信开放平台&#xff08;APP&#xff09;等等。 【…