linux各种锁机制的使用及区别

前言:相信需要了解这方面的知识的小伙伴,已经基本对进程间通信和线程间通信有了一定了解。例如,进程间通信的机制之一:共享内存(在这里不做详解):多个进程可同时访问同一块内存。如果不对访问这块内存的临界区进行互斥或者同步,那么进程的运行很可能出现一些不可预知的错误和结果。接下来我们了解三种常见的Linux下的互斥操作—>锁。

1.互斥锁(mutex)
    特点:对于读者和写者来说。只要有一方获取了锁,另一方则不能继续获取,进而执行临界区代码。
    创建锁:
    有两种方法创建互斥锁,静态方式和动态方式。POSIX定义了一个宏PTHREAD_MUTEX_INITIALIZER 来静态初始化互斥锁,方法如下:
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
在LinuxThreads实现中,pthread_mutex_t是一个结构,而PTHREAD_MUTEX_INITIALIZER则是一个结构常量。
动态方式是采用pthread_mutex_init()函数来初始化互斥锁,API定义如下:
 int pthread_mutex_init(pthread_mutex_t  *mutex, const pthread_mutexattr_t*mutexattr)
其中mutexattr用于指定互斥锁属性(见下),如果为NULL则使用缺省属性。 pthread_mutex_destroy ()用于注销一个互斥锁,API定义如下:
 int pthread_mutex_destroy(pthread_mutex_t *mutex)
锁操作主要包括加锁pthread_mutex_lock()、解锁pthread_mutex_unlock()和测试加锁 pthread_mutex_trylock()三个,不论哪种类型的锁,都不可能被两个不同的线程同时得到, 而必须等待解锁。对于普通锁和适应锁类型,解锁者可以是同进程内任何线程; 而检错锁则必须由加锁者解锁才有效,否则返回EPERM;对于嵌套锁,文档和实现要求必须由 加锁者解锁,但实验结果表明并没有这种限制,这个不同目前还没有得到解释。在同一进程中 的线程,如果加锁后没有解锁,则任何其他线程都无法再获得锁。

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

pthread_mutex_trylock() 语义与pthread_mutex_lock()类似,不同的是在锁已经被占据时返回 EBUSY而不是挂起等待。
例如:单例模式下,线程安全的加锁:
class SingleTon
{
  public:
  static SingleTon* getInstance()
  {
      pthread_mutex_lock(&mutex);
      if(mpSingle == NULL)
      {
          mpSingleTon = new SingleTon();
       }
      pthread_mutex_unlock(&mutex);
       return mpSingleTon;
   }
  private:
   SingleTon(){};
   ~SingleTon(){pthread_mutex_desttroy(&mutex,NULL);}
    static pthread_mutex_t mutex;
    static SingleTon * mpSingleTon; 
}
pthread_mutex_t SingleTon::mutex = PTHREAD_MUTEX_INITIALIZER;
SingleTon * SingleTon::mpSingleTon = NULL;

优点:由一块能够被多个进程共享的内存空间(一个对齐后的整型变量)组成;这个整型变量的值能够通过汇编语言调用CPU提供的原子操作指令来增加或减少,并且一个进程可以等待直到那个值变成正数。 的操作几乎全部在应用程序空间完成;只有当操作结果不
一致从而需要仲裁时,才需要进入操作系统内核空间执行。这种机制允许使用的锁定原语有非常高的执行效率:由于绝大多数
的操作并不需要在多个进程之间进行仲裁,所以绝大多数操作都可以在应用程序空间执行,而不需要使用(相对高代价的)内核系统调
用。

2.读写锁
    特点:读写锁适合于对数据结构的读次数比写次数多得多的情况.因为,读模式锁定时可以共享,以写 模式锁住时意味着独占,所以读写锁又叫共享-独占锁.
    初始化和销毁:
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const
    pthread_rwlockattr_t *restrict attr);
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
成功则返回0,出错则返回错误编号. 同互斥量以上,在释放读写锁占用的内存之前,需要先通过 pthread_rwlock_destroy对读写锁进行清理工作, 释放由init分配的资源.
读和写:
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
成功则返回0,出错则返回错误编号.这3个函数分别实现获取读锁,获取写锁和释放锁的操作.获 取锁的两个函数是阻塞操作,同样,非阻塞的函数为:
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
成功则返回0,出错则返回错误编号.非阻塞的获取锁操作,如果可以获取则返回0,否则返回 错误的EBUSY.

3.自旋锁
     特点:轮询忙等待。
               在单核cpu下不起作用:被自旋锁保护的临界区代码执行时不能进行挂起状态。会造成死锁
               自旋锁的初衷就是:在短期间内进行轻量级的锁定。一个被争用的自旋锁使得请求它的线程在等待锁重新可用的期间进行自旋(特别浪费处理器时间),所以自旋锁不应该被持有时间过长。如果需要长时间锁定的话, 最好使用信号量。
        API:

4条件变量

条件变量参考这里。

与互斥锁不同,条件变量是用来等待而不是用来上锁的。条件变量用来自动阻塞一个线程,直 到某特殊情况发生为止。通常条件变量和互斥锁同时使用。

条件变量使我们可以睡眠等待某种条件出现。条件变量是利用线程间共享的全局变量进行同步 的一种机制,主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起;另一个线程使 "条件成立"(给出条件成立信号)。

条件的检测是在互斥锁的保护下进行的。如果一个条件为假,一个线程自动阻塞,并释放等待 状态改变的互斥锁。如果另一个线程改变了条件,它发信号给关联的条件变量,唤醒一个或多 个等待它的线程,重新获得互斥锁,重新评价条件。如果两进程共享可读写的内存,条件变量 可以被用来实现这两进程间的线程同步。相关的函数如下:

int pthread_cond_init(pthread_cond_t *cond,pthread_condattr_t *cond_attr);
int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);
int pthread_cond_timewait(pthread_cond_t *cond,pthread_mutex *mutex,consttimespec *abstime);
int pthread_cond_destroy(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);  //解除所有线程的阻塞

简要说明:

  1. 初始化.init()或者pthread_cond_t cond=PTHREAD_COND_INITIALIER;属性置为NULL
  2. 等待条件成立.pthread_wait,pthread_timewait.wait()释放锁,并阻塞等待条件变量为真 timewait()设置等待时间,仍未signal,返回ETIMEOUT(加锁保证只有一个线程wait)
  3. 激活条件变量:pthread_cond_signal,pthread_cond_broadcast(激活所有等待线程)
  4. 清除条件变量:destroy;无线程等待,否则返回EBUSY

条件变量与互斥锁、信号量的区别

  1. 互斥锁必须总是由给它上锁的线程解锁,信号量的挂出即不必由执行过它的等待操作的同一 进程执行。一个线程可以等待某个给定信号灯,而另一个线程可以挂出该信号灯。
  2. 互斥锁要么锁住,要么被解开(二值状态,类型二值信号量)。
  3. 由于信号量有一个与之关联的状态(它的计数值),信号量挂出操作总是被记住。然而当向 一个条件变量发送信号时,如果没有线程等待在该条件变量上,那么该信号将丢失。
  4. 互斥锁是为了上锁而设计的,条件变量是为了等待而设计的,信号灯即可用于上锁,也可用 于等待,因而可能导致更多的开销和更高的复杂性。

信号量、互斥锁和自旋锁的区别

信号量。互斥锁允许进程sleep属于睡眠锁,自旋锁不允许调用者sleep,而是让其循环等待,所以有以下区别应用:

  1. 信号量和读写信号量适合于保持时间较长的情况,它们会导致调用者睡眠,因而自旋锁适合于保持时间非常短的情况;
  2. 自旋锁可以用于中断,不能用于进程上下文(会引起死锁),而信号量不允许使用在中断中,而可以用于进程上下文;
  3. 自旋锁保持期间是抢占失效的,自旋锁被持有时,内核不能被抢占,而信号量和读写信号量保持期间是可以被抢占的。

另外需要注意的是:

  1. 信号量锁保护的临界区可包含可能引起阻塞的代码,而自旋锁则绝对要避免用来保护包含这样代码的临界区,因为阻塞意味着要进行进程的切换,如果进程被切换出去后,另一进程企图获取本自旋锁,死锁就会发生;
  2. 占用信号量的同时不能占用自旋锁,因为在等待信号量时可能会睡眠,而在持有自旋锁时是不允许睡眠的。

信号量和互斥锁的区别

1、概念上的区别:

信号量:是进程间(线程间)同步用的,一个进程(线程)完成了某一个动作就通过信号量告诉别的进程(线程),别的进程(线程)再进行某些动作。有二值和多值信号量之分;

互斥锁:是线程间互斥用的,一个线程占用了某一个共享资源,那么别的线程就无法访问,直到这个线程离开,其他的线程才开始可以使用这个共享资源。可以把互斥锁看成二值信号量。

2、上锁时:

信号量: 只要信号量的value大于0,其他线程就可以sem_wait成功,成功后信号量的value减一。若value值不大于0,则sem_wait阻塞,直到sem_post释放后value值加一。一句话,信号量的value>=0。

互斥锁: 只要被锁住,其他任何线程都不可以访问被保护的资源。如果没有锁,获得资源成功,否则进行阻塞等待资源可用。一句话,线程互斥锁的vlaue可以为负数。

3、使用场所:

信号量主要适用于进程间通信,当然,也可用于线程间通信。而互斥锁只能用于线程间通信。

linux几种锁的分析与比较相关推荐

  1. 浅谈Java中15种锁的分析比较

    作者:站长,来自:搜云库技术团队 在读很多并发文章中,会提及各种各样锁如公平锁,乐观锁等等,这篇文章介绍各种锁的分类,介绍的内容如下: 公平锁 / 非公平锁 可重入锁 / 不可重入锁 独享锁 / 共享 ...

  2. openVswitch(OVS)源代码之linux RCU锁机制分析

    前言 本来想继续顺着数据包的处理流程分析upcall调用的,但是发现在分析upcall调用时必须先了解linux中内核和用户空间通信接口Netlink机制,所以就一直耽搁了对upcall的分析.如果对 ...

  3. oracle degree 造成阻塞_数据库锁/阻塞分析的一种常用方法

    对于一些中小用户来说,日常遇到的最多的问题不外乎表空间与锁的分析.数据库表空间使用率/空间使用率分析似乎很简单,是一条SQL就能搞定的事情,实际上并不简单,这个话题容以后找时间阐述.今天就重点分析下另 ...

  4. Linux五种IO模型性能分析

    转载:http://blog.csdn.net/jay900323/article/details/18141217     Linux五种IO模型性能分析 目录(?)[-] 概念理解 Linux下的 ...

  5. linux 内核 死锁 检查,一种linux内核自旋锁死锁检测报告系统和方法与流程

    本发明涉及内核死锁检测领域,具体的说是一种linux内核自旋锁死锁检测报告系统和方法. 背景技术: linux内核死锁是长期困扰内核开发人员的问题之一,但自内核引入lockdep调试模块之后,内核死锁 ...

  6. Windows五种IO模型性能分析和Linux五种IO模型性能分析

    Windows五种IO模型性能分析和Linux五种IO模型性能分析 http://blog.csdn.net/jay900323/article/details/18141217 http://blo ...

  7. linux物理内存地址与iomem,一种Linux系统物理内存镜像文件分析方法_4

    模块信息,如图7所示,给出了本发明的实施例中 模块结构关系图,modules变量指向某一个已加载模块结构体module地址,所有已加载模 块其module形成一个双向链表,如图7所示,据此可以获取到所 ...

  8. 通过三种情况深度分析,复杂的公网环境,网络穿透如何做到?丨C++后端开发丨P2P丨c/c++Linux服务器开发丨网关API

    通过三种情况深度分析,复杂的公网环境,网络穿透如何做到? 视频讲解如下,点击观看: 通过三种情况深度分析,复杂的公网环境,网络穿透如何做到?丨C++后端开发丨P2P丨c/c++Linux服务器开发丨网 ...

  9. 基于Exynos4412的嵌入式Linux的几种启动方式分析

    Exynos4412的几种启动方式分析 启动流程分析 BL1(BootLoader)的读入方式 操作系统OS内核的读入方式 小结 启动流程分析 在芯片的iROM中已经固化一个代码,当硬件上电后就读取O ...

  10. Linux内核中锁机制之完成量、互斥量

    在上一篇博文中笔者分析了关于信号量.读写信号量的使用及源码实现,接下来本篇博文将讨论有关完成量和互斥量的使用和一些经典问题. 八.完成量 下面讨论完成量的内容,首先需明确完成量表示为一个执行单元需要等 ...

最新文章

  1. java 工厂 单例_java 单例模式和工厂模式实例详解
  2. jps could not synchronize with target
  3. spider-定向抓取
  4. 第一行代码学习笔记第十章——探究服务
  5. 配置Struts2.0
  6. 【论文翻译】统一知识图谱学习和建议:更好地理解用户偏好
  7. php仿微信朋友圈网站源码,Smobiler仿微信朋友圈的消息代码实例
  8. 在ASP.net中的UpdatePanel,弹窗失败解决办法
  9. server 2008r2 rabbitmq 安装web管理
  10. 我的世界网易版显示dns服务器错误怎么弄,手机显示DNS异常怎么办
  11. 移动宽带连接不上mysql_MySQL Workbench 6.2.4 下载 因为移动网络打不开官方也可能是国内网络问题 备用自己...
  12. java clone()用法_java clone方法使用详解(转)
  13. android 人脸识别边框_Android实现简单的人脸识别
  14. sql面试题:问题1:查询每个同学的学生编号、学生姓名、选课总数...问题2:查询“张三”老师所授课程的学生中,成绩最高的学生信息...
  15. 淘宝评论不回复买家怎样看到买家评论,如何提前处理买家差评
  16. uni-app实现一键登录
  17. 银行卡支付之连连支付
  18. ACP.敏捷概念梳理1
  19. python中关于Process finished with exit code -1073740791 (0xC0000409)的解决办法
  20. 分布式框架DSF的搭建

热门文章

  1. 手把手教你制作gif动图,一分钟轻松学会
  2. json 格式字符串
  3. Tone mapping进化论
  4. 为什么要做特征共线性分析
  5. 大厂程序员因厌恶编程,辞去月薪2w+的工作去当司机?
  6. 多元函数的泰勒展开公式
  7. Illustrator CS5序列号
  8. Java从遗忘到入门——Day06
  9. 时钟同步服务器性能,GPS时间同步概述-GPS同步时钟的适应范围-GPS同步的优缺点-NTP服务器|同步时钟|子母钟|数显钟|GPS时间同步...
  10. 富文本编辑器kindeditor支持从word复制粘贴保留格式和图片的插件