Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Linux线程互斥是如何实现的

Linux线程互斥是如何实现的

作者头像
用户6754675
修改于 2020-07-16 06:32:46
修改于 2020-07-16 06:32:46
1.6K0
举报
文章被收录于专栏:嵌入式单片机嵌入式单片机

 一、互斥锁

  为啥要有互斥?

  多个进程/线程执行的先后顺序不确定,何时切出CPU也不确定。

  多个进程/线程访问变量的动作往往不是原子的。

  1. 操作步骤

  (1)创建锁

  // 创建互斥锁mutex

  pthread_mutex_t mutex;

  (2)初始化锁

  在Linux下, 线程的互斥量数据类型是pthread_mutex_t 在使用前, 要对它进行初始化:

  初始化的两种方法:(推荐使用第二种)

  1.静态分配

  pthread_mutex mutex = PTHREAD_MUTEX_INITIALIZER;

  2.动态分配

  int pthread_mutex_init(pthread_mutex_t *restrict mutex, const

pthread_mutexattr_t *restrict attr);

  mutex: 要初始化的互斥量(restrict的作用是告诉调用者,不要改变指针的指向)

  attr:锁的属性,一般写NULL

  加restrict的作用:只用于修饰函数参数里的指针,这个指针会频繁使用,所以把这个地址放到寄存器里,用着好找。

  ①设置线程的属性

  int pthread_attr_init(pthread_attr_t *attr);//初始化线程属性

  int pthread_attr_destroy(pthread_attr_t *attr);//销毁线程属性

  Thread attributes(线程属性):

  线程的分离属性: Detach state=PTHREAD_CREATE_DETACHED

  线程的竞争范围: Scope = PTHREAD_SCOPE_SYSTEM

  是否继承调度策略: Inherit scheduler = PTHREAD_EXPLICIT_SCHED

  调度策略: Scheduling policy = SCHED_OTHER

  调度优先级: Scheduling priority = 0

  线程栈之间的保留区域: Guard size = 4096 bytes

  自己指定栈地址: Stack address = 0x40197000

  栈大小: Stack size = 0x3000000 bytes

  //设置线程的分离属性

  int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);

  //detachstate:有以下两种选择

  PTHREAD_CREATE_DETACHED:设置成分离态

  PTHREAD_CREATE_JOINABLE:设置成可结合态

  //获取线程的分离属性

  int pthread_attr_getdetachstate(pthread_attr_t *attr, int

*detachstate);

  //int *detachstate:输出型参数,将分离属性存放在该变量里

  (3)上锁 && 解锁

  对共享资源的访问, 要对互斥量进行加锁, 如果互斥量已经上了锁, 调用线程会阻塞, 直到互斥量被解锁.

在完成了对共享资源的访问后,要对互斥量进行解锁。

  具体说一下trylock函数, 这个函数是非阻塞调用模式, 也就是说, 如果互斥量没被锁住, trylock函数将把互斥量加锁,

并获得对共享资源的访问权限; 如果互斥量被锁住了, trylock函数将不会阻塞等待而直接返回EBUSY, 表示共享资源处于忙状态。

  锁粒度(尽量小):临界区操作个数,执行时间的长短

  int pthread_mutex_lock(pthread_mutex_t *mutex);

  //加锁:如果是1,置0,返回

  // 如果是0,阻塞

  int pthread_mutex_trylock(pthread_mutex_t *mutex);//非阻塞加锁

  int pthread_mutex_unlock(pthread_mutex_t *mutex); //解锁

  返回值: 成功则返回0, 出错则返回错误编号.

  (4)销毁互斥锁

  对于动态分配的互斥量, 在申请内存(malloc)之后, 通过pthread_mutex_init进行初始化,

并且在释放内存(free)前需要调用pthread_mutex_destroy.

  注意:

  使用PTHREAD_MUTEX_INITIALIZER初始化的互斥量不需要销毁

  不要销毁一个已经加锁的互斥量

  已销毁的互斥量要确保后面不会有线程尝试加锁

  pthread_mutex_destroy(&lock); //销毁

  返回值: 成功则返回0, 出错则返回错误编号.

  说明: 如果使用默认的属性初始化互斥量, 只需把attr设为NULL. 其他值在以后讲解。

  2. 死锁

  (1)死锁的两种情况:

  情况1:

  如果两个线程先后调用两次lock,第二次调用lock时,由于锁已被占用,该线程会挂起等待别的线程释放锁,然后锁正是被自己占用着的,该线程又被挂起不能释放锁,因此就永远处于挂起等待状态了,这就叫死锁。

  情况2:

  有线程A、B。A获得锁1,B获得锁2,此时A调用lock企图获得锁2,结果是需要挂起等待B释放锁2,而此时B也调用了lock企图获得锁1,结果是B挂起等待A释放锁1,于是乎A、B永远处于挂起状态。

  (2)避免的死锁的原则

  死锁主要发生在有多个依赖锁存在时,会在一个线程试图以与另一个线程相反顺序锁住互斥量时发生.如何避免死锁是使用互斥量应该格外注意的东西。

  总体来讲, 有几个不成文的基本原则:

  对共享资源操作前一定要获得锁。

  完成操作以后一定要释放锁。

  尽量短时间地占用锁。

  如果有多锁, 如获得顺序是ABC连环扣, 释放顺序也应该是ABC。

  线程错误返回时应该释放它所获得的锁。

  写程序是尽量避免同时获得多个锁,如果一定要这么做,则遵循一个原则:如果所有线程在需要多个锁时都按相同的先后顺序(常见是按mutex变量的地址顺序)获得锁,则不会出现死锁。

  mutex互斥信号量锁住的不是一个变量,而是阻塞住一段程序。如果对一个mutex变量testlock,执行了第一次pthread_mutex_lock(testlock)之后,在unlock之前的这段时间内,如果有其他线程也执行到了pthread_mutex_lock,这个线程就会阻塞住,直到之前的线程unlock之后才能执行,由此,实现同步,也就达到保护临界区资源的目的。

  为了实现互斥操作,大多数体系结构提供了swap或exchange指令,该指令的作用是把寄存器和内存单元的数据交换,由于只有一条指令,保证了原子性。即使是多处理器平台,访问内存的总线周期也有先后,一个处理器的交换指令执行时另一个处理器的交换指令只能等待总线周期。

  (3)临界区代码原则

  短——临界区代码简洁明了;

  平——临界区代码逻辑清晰,没有复杂的函数调用尤其是尽量不要申请其他互斥资源;

  快:临界区代码执行速度快。

  3. 互斥锁和信号量的区别

  互斥量用于线程的互斥,信号线用于线程的同步。

  这是互斥量和信号量的根本区别,也就是互斥和同步之间的区别。

  互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。

  同步:是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源

  互斥量值只能为0/1,信号量值可以为非负整数。

  也就是说,一个互斥量只能用于一个资源的互斥访问,它不能实现多个资源的多线程互斥问题。信号量可以实现多个同类资源的多线程互斥和同步。当信号量为单值信号量是,也可以完成一个资源的互斥访问。

  互斥量的加锁和解锁必须由同一线程分别对应使用,信号量可以由一个线程释放,另一个线程得到。

  4. 线程安全和可重入

  可重入函数:在多个执行流中被同时调用不会存在问题。

  线程安全函数:在多线程中被同时调用不会存在问题。

  可重入函数一般情况下都是线程安全的

  线程安全函数不一定是可重入函数

  二、自旋锁

  1. 操作步骤

  //1. 定义自旋锁

  pthread_spinlock_t spin;

  //2. 初始化自旋锁

  int pthread_spin_init(pthread_spinlock_t *lock, int pshared);

  //3. 上锁

  int pthread_spin_lock(pthread_spinlock_t *lock);

  int pthread_spin_trylock(pthread_spinlock_t *lock);

  //4. 解锁

  int pthread_spin_unlock(pthread_spinlock_t *lock);

  //5. 销毁锁

  int pthread_spin_destroy(pthread_spinlock_t *lock);

  2. 自旋锁和互斥锁的区别

  互斥锁是当阻塞在pthread_mutex_lock时,放弃CPU,好让别人使用CPU。自旋锁阻塞在pthread_spin_lock时,不会释放CPU,不断向cup询问可以用了不。

  三、读写锁

  1. 读写锁的规则

  读读共享

  读写排他

  写写排他

  写优先级高

  2. 操作步骤

  // 1. 定义锁

  pthread_rwlock_t lock;

  // 2. 初始化

  pthread_rwlock_init(&lock, NULL);

  // 3. 读锁

  pthread_rwlock_rdlock(&lock);

  pthread_rwlock_wrlock(&lock);

  // 4. 解锁

  pthread_rwlock_unlock(&lock);

  // 5. 销毁锁

  pthread_rwlock_destroy(&lock);

  3. 代码实现

  #include

  #include

  #include

  #include

  int g_count;

  pthread_rwlock_t rw;

  void* route_read(void* arg)

  {

  int id = (int)arg;

  free(arg);

  while(1)

  {

  pthread_rwlock_rdlock(&rw);

  printf("%d read_pthread : %d\n", id, g_count);

  pthread_rwlock_unlock(&rw);

  usleep(100000);

  }

  }

  void* route_write(void* arg)

  {

  int id = (int)arg;

  free(arg);

  while(1)

  {

  pthread_rwlock_wrlock(&rw);

  printf("%d write_pthread : %d\n", id, ++g_count);

  pthread_rwlock_unlock(&rw);

  usleep(100000);

  }

  }

  int main()

  {

  pthread_t w1, r1, r2, r3;

  pthread_rwlock_init(&rw, NULL);

  int* p = (int*)malloc(sizeof(int));

  *p = 1;

  pthread_create(&w1, NULL, route_write, p);

  int* p1 = (int*)malloc(sizeof(int));

  *p1 = 1;

  pthread_create(&r1, NULL, route_read, p1);

  int* p2 = (int*)malloc(sizeof(int));

  *p2 = 2;

  pthread_create(&r2, NULL, route_read, p2);

  int* p3 = (int*)malloc(sizeof(int));

  *p3 = 3;

  pthread_create(&r3, NULL, route_read, p3);

  pthread_join(w1, NULL);

  pthread_join(r1, NULL);

  pthread_join(r2, NULL);

  pthread_join(r3, NULL);

  pthread_rwlock_destroy(&rw);

  }

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
作者已关闭评论
暂无评论
推荐阅读
编辑精选文章
换一批
Linux 线程间通信和同步
很多时候,我们做项目并不会创建那么多进程,而是创建一个进程,在该进程中创建多个线程进行工作。
Jasonangel
2022/10/25
1.6K0
Linux 线程间通信和同步
如何理解互斥锁、条件变量、读写锁以及自旋锁?
锁是一个常见的同步概念,我们都听说过加锁(lock)或者解锁(unlock),当然学术一点的说法是获取(acquire)和释放(release)。
果冻虾仁
2021/12/08
1.6K0
如何理解互斥锁、条件变量、读写锁以及自旋锁?
linux中实现线程同步的6种方法
最后运行的结果不是固定的,有可能是0、-1,如果有这个ticket_num变量代表是库存的话,那么就会出现库存为负数的情况,所以需要引入线程同步来保证线程安全。
全栈程序员站长
2022/09/14
9140
线程同步与互斥
不是什么时候都要靠上锁的。从根源出发,我们为什么需要上锁?因为线程在使用资源的过程中可能会出现冲突,对于这种会出现冲突的资源,还是锁住轮着用比较好。
看、未来
2021/10/09
8850
线程同步与互斥
详解Linux多线程中互斥锁、读写锁、自旋锁、条件变量、信号量
---- Hello、Hello大家好,我是木荣,今天我们继续来聊一聊Linux中多线程编程中的重要知识点,详细谈谈多线程中同步和互斥机制。 同步和互斥 互斥:多线程中互斥是指多个线程访问同一资源时同时只允许一个线程对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的; 同步:多线程同步是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源
Linux兵工厂
2023/02/28
3.9K0
详解Linux多线程中互斥锁、读写锁、自旋锁、条件变量、信号量
Linux线程-互斥与同步
Linux互斥与同步 零、前言 一、Linux线程互斥 1、基本概念及引入 2、互斥量mutex介绍 3、互斥量的使用 4、互斥量原理 二、可重入/线程安全 1、基本概念 2、线程安全 3、重入函数 4、联系与区别 三、常见锁概念 四、Linux线程同步 1、基本概念 2、条件变量的使用 3、条件变量等待 4、条件变量使用规范 五、POSIX信号量 1、信号量概念及介绍 2、信号量的使用 零、前言 本章主要讲解学习Linux中对多线程的执行中的同步与互斥 一、Linux线程互斥 1、基本概念及引入 互
用户9645905
2022/11/30
1.8K0
Linux线程-互斥与同步
Linux内核编程--进程控制,线程控制,锁机制
每个进程都有一个非负整型表示的唯一进程ID。进程ID是可复用的,当一个进程终止后,其进程ID也会被其他进程使用。
Coder-ZZ
2022/05/09
1.2K0
Linux内核编程--进程控制,线程控制,锁机制
多线程锁有几种类型_进程同步和互斥概念
现代操作系统基本都是多任务操作系统,即同时有大量可调度实体在运行。在多任务操作系统中,同时运行的多个任务可能:
全栈程序员站长
2022/09/22
1.3K0
多线程锁有几种类型_进程同步和互斥概念
【转】自旋锁spin和互斥量mutex的区别
在MySQL种,执行show engine innodb status \G 经常会看到里面有spin lock 及mutex的情况。我们有必要了解下这些知识。
保持热爱奔赴山海
2019/09/17
2.6K0
【Linux线程】Linux多线程编程:深入理解线程互斥与同步机制
🔍前言:在当今这个数据驱动、高性能计算盛行的时代,多线程编程已经成为软件开发中不可或缺的一部分。Linux,作为开源世界的领头羊,其强大的多线程支持为开发者提供了广阔的舞台,让高并发、高性能的应用得以实现。然而,多线程编程也是一把双刃剑,它在带来性能提升的同时,也引入了线程安全、资源竞争等复杂问题
Eternity._
2024/10/19
2970
【Linux线程】Linux多线程编程:深入理解线程互斥与同步机制
Linux同步机制(一) - 线程锁
1 互斥锁 在线程实际运行过程中,我们经常需要多个线程保持同步。 这时可以用互斥锁来完成任务。互斥锁的使用过程中,主要有 pthread_mutex_init pthread_mutex_destory pthread_mutex_lock pthread_mutex_unlock 这几个函数以完成锁的初始化,锁的销毁,上锁和释放锁操作。 1.1 锁的创建 锁可以被动态或静态创建,可以用宏PTHREAD_MUTEX_INITIALIZER来静态的初始化锁,采用这种方式比较容易理解,互斥锁是pthread_m
三丰SanFeng
2018/01/16
3.6K0
【Linux】一篇文章带你了解Linux多线程&&线程控制&&线程安全&&线程互斥详解
进程的多个线程共享 同一地址空间,因此Text Segment、Data Segment都是共享的,如果定义一个函数,在各线程中都可以调用,如果定义一个全局变量,在各线程中都可以访问到,除此之外,各线程还共享以下进程资源和环境:
用户10925563
2024/08/29
3130
【Linux】一篇文章带你了解Linux多线程&&线程控制&&线程安全&&线程互斥详解
并发问题解密:探索多线程和锁机制
描述: pthread_create()函数在调用进程中启动一个新线程。新线程通过调用start_routine()开始执行;arg作为start_routine()的唯一参数传递。
Lion 莱恩呀
2024/09/26
2700
并发问题解密:探索多线程和锁机制
Linux线程同步与互斥(一)
线程的大部分资源是共享的,包括定义的全局变量等等,全局变量是能够让全部线程共享的。大部分资源共享带来的优点是通信方便,缺点是缺乏访问控制,同时如果因为一个线程的操作问题,给其它线程造成了不可控,或者引起崩溃异常,逻辑不正确等现象,就会造成线程安全问题!所有需要进行后续的访问控制:同步与互斥!
二肥是只大懒蓝猫
2023/03/30
1.4K0
Linux线程同步与互斥(一)
自旋锁和互斥锁区别在哪_互斥锁的实现
POSIX threads(简称Pthreads)是在多核平台上进行并行编程的一套常用的API。线程同步(Thread Synchronization)是并行编程中非常重要的通讯手段,其中最典型的应用就是用Pthreads提供的锁机制(lock)来对多个线程之间共 享的临界区(Critical Section)进行保护(另一种常用的同步机制是barrier)。
全栈程序员站长
2022/11/01
1.1K0
Linux线程同步与互斥
  如果不能保持互斥,那么会发生一些不合逻辑的事情,以下面这段多线程抢票代码为例:
用户11029129
2024/09/24
1610
Linux线程同步与互斥
【Pthreads学习笔记】基本使用
与OpenMP相比,Pthreads的使用相对要复杂一些,需要我们显式的创建、管理、销毁线程,但也正因为如此,我们对于线程有更强的控制,可以更加灵活的使用线程。这里主要记录一下Pthreads的基本使用方法,如果不是十分复杂的使用环境,这些知识应该可以了。本文大部分内容都是参考自这里,有兴趣的可以看一下原文。
零式的天空
2022/03/22
7230
多线程安全-iOS开发注意咯!!!
但是并不是非常完美,因为多线程常常伴有资源抢夺的问题,作为一个高级开发人员并发编程那是必须要的,同时解决线程安全也成了我们必须要要掌握的基础
iOSSir
2019/05/08
9070
多线程编程C语言版
什么是多线程,提出这个问题的时候,我还是很老实的拿出操作系统的书,按着上面的话敲下“为了减少进程切换和创建开销,提高执行效率和节省资源,我们引入了线程的概念,与进程相比较,线程是CPU调度的一个基本单位。”
DeROy
2021/11/16
3.7K0
多线程编程C语言版
Linux下多线程编程详解简介
上面的代码很简单,就是启动一个线程,然后先线程里循环打印字段字符串。我们就以这个最简单的例子来开口。
用户2929716
2018/08/23
4.3K0
相关推荐
Linux 线程间通信和同步
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档