自旋锁(Spin Lock)

自旋锁类似于互斥量,不过自旋锁不是通过休眠阻塞进程,而是在取得锁之前一直处于忙等待的阻塞状态。这个忙等的阻塞状态,也叫做自旋。

自旋锁通常作为底层原语实现其他类型的锁。

适用场景:

1)锁被持有的时间短,而且线程不希望在重新调度上花费太多的成本;

2)在非抢占式内核中,会阻塞中断,这样中断处理程序不会让系统陷入死锁状态。因为中断处理程序无法休眠,只能使用这种锁;

缺点:

1)线程自旋等待锁变成可用时,CPU不能做其他事情,会浪费CPU资源;

伪代码

S = 1

线程P:
// 进入区
while (S <= 0) ; // 自旋
S--; // P操作 ... // 临界区 // 退出区
S++; // V操作

自旋锁接口

自旋锁接口与互斥量类似,容易相互替换。

#include <pthread.h>

int pthread_spin_init(pthread_spinlock_t *lock, int pshared);

int pthread_spin_destroy(pthread_spinlock_t *lock);

int pthread_spin_lock(pthread_spinlock_t *lock);
int pthread_spin_trylock(pthread_spinlock_t *lock);
int pthread_spin_unlock(pthread_spinlock_t *lock);

注意:

1)如果自旋锁当前在解锁状态,pthread_spin_lock不用自旋,就可以对它加锁;

2)如果自旋锁当前在加锁状态,再获得锁的结果是未定义的。如果调用pthread_spin_lock,会返回EDEADLK错误或其他错误,或者调用者可能会永久自旋。取决于具体实现。

3)试图对没有加锁的自旋锁解锁,结果也是未定义的。

示例

自旋锁使用

#include <pthread.h>
#include <stdio.h> #define THREAD_NUM 100 pthread_spinlock_t spinlock; void *thread_main(void *arg)
{
int id = (int)arg; pthread_spin_lock(&spinlock); // 获得锁
printf("thread main %d get the lock begin\n", id);
printf("thread main %d get the lock end\n", id); pthread_spin_unlock(&spinlock); // 释放锁
return NULL;
} int main()
{
pthread_spin_init(&spinlock, 0); /* PTHREAD_PROCESS_PRIVATE == 0*/ int x = PTHREAD_PROCESS_PRIVATE;
printf("x = %d\n", x); int i;
pthread_t tids[THREAD_NUM];
for (i = 0; i < THREAD_NUM; i++) {
pthread_create(&tids[i], NULL, thread_main, i); // 创建线程
} for (i = 0; i < THREAD_NUM; i++) {
pthread_join(tids[i], NULL); // 连接线程
} return 0;
}

互斥量(互斥锁, Mutex)

互斥量(Mutex)通过休眠阻塞进程/线程,确保同一时间只有一个线程访问数据。休眠,也就意味着会放弃CPU资源。

加锁

对互斥量加锁后,任何其他试图再次对互斥量加锁的线程,都会被阻塞,直到当前线程释放该互斥锁。

解锁

如果阻塞在该互斥锁上的线程有多个,当锁可用时,所有线程都会变成可运行状态,第一个变为运行的线程,就可以对互斥量加锁,其他线程则再次等待锁而进入休眠。

适用场景

多线程或多进程运行环境,需要对临界区资源进行保护时。

缺点

1)使用不当,任意导致死锁;

2)无法表示临界区资源可用数量(由信号量解决);

接口

#include <pthread.h>

int pthread_mutex_destroy(pthread_mutex_t *mutex);
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr); // 函数方式初始化,attr是线程属性
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // 直接赋值方式初始化 int pthread_mutex_lock(pthread_mutex_t *mutex); // 加锁
int pthread_mutex_trylock(pthread_mutex_t *mutex); // 尝试加锁,不会阻塞
int pthread_mutex_unlock(pthread_mutex_t *mutex); // 解锁

使用示例

#define THREAD_NUM 100

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

void *thread_main(void *arg)
{
int id = (int)arg; pthread_mutex_lock(&mutex);
printf("thread main %d get the lock begin\n", id);
printf("thread main %d get the lock end\n", id); pthread_mutex_unlock(&mutex);
return NULL;
} int main()
{
int i;
pthread_t tids[THREAD_NUM];
for (i = 0; i < THREAD_NUM; i++) {
pthread_create(&tids[i], NULL, thread_main, i);
} for (i = 0; i < THREAD_NUM; i++) {
pthread_join(tids[i], NULL);
} return 0;
}

读写锁(Read-Write Lock)

读写锁类似于互斥量,不过读写锁允许更高的并行性。读写锁,也叫共享互斥锁(shared-exclusive lock)。

当读写锁以读模式锁住时,可以说成是以共享模式锁住的。当以写模式锁住时,可以说成是以互斥模式锁住的。

读写锁与互斥锁的区别

读写锁与互斥锁的区别在于:

互斥锁 要么是加锁状态,要么是不加锁状态,而且一次只有一个线程能取得锁、对其加锁;

读写锁 可以有3种状态:读模式加锁,写模式加锁,不加锁。一次只有一个线程能占有写模式的读写锁,不过多个线程可以同时占有读模式的读写锁。

1)当读写锁是写加锁状态时,在被解锁前,所有试图对其加锁的线程都会被阻塞。

2)当读写锁是读加锁状态时,在被解锁前,所有以读模式加锁的线程都可以得到访问权,以写模式加锁的线程会被阻塞。

简而言之,读写锁是读状态与读状态之间共享,与写状态之间互斥,写状态是与任何状态互斥。

互斥锁是只有加锁和解锁状态,加锁状态之间互斥。

适用场景

读写锁非常适合对数据结构进行读操作的次数 远大于写的情况。

使用接口

初始化销毁:

#include <pthread.h>

int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
const pthread_rwlockattr_t *restrict attr); pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; // 直接赋值方式初始化读写锁

attr = NULL,表示使用默认的读写锁属性。

读、写模式获得锁,解锁:

#include <pthread.h>

int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); // 读模式取得锁
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock); // 读模式取得锁的条件版本 int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock); // 写模式取得锁
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); // 写模式取得锁的条件版本 int pthread_rwlock_unlock(pthread_rwlock_t *rwlock); // 解锁

注意:

1)不论是处于写模式,还是读模式,都可以用pthread_rwlock_unlock解锁。

2)条件版本不会阻塞线程。

Linux 自旋锁,互斥量(互斥锁),读写锁的更多相关文章

  1. Linux的线程同步对象:互斥量Mutex,读写锁,条件变量

        进程是Linux资源分配的对象,Linux会为进程分配虚拟内存(4G)和文件句柄等 资源,是一个静态的概念.线程是CPU调度的对象,是一个动态的概念.一个进程之中至少包含有一个或者多个线程.这 ...

  2. pthread中互斥量,锁和条件变量

    互斥量 #include <pthread.h> pthread_mutex_t mutex=PTHREAD_MUTEX_INTIIALIZER; int pthread_mutex_in ...

  3. go Mutex (互斥锁)和RWMutex(读写锁)

    转载自: https://blog.csdn.net/skh2015java/article/details/60334437 golang中sync包实现了两种锁Mutex (互斥锁)和RWMute ...

  4. Linux环境编程之同步(三):读写锁

    概述 相互排斥锁把试图进入我们称之为临界区的全部其它线程都堵塞住.该临界区通常涉及对由这些线程共享一个或多个数据的訪问或更新.读写锁在获取读写锁用于读某个数据和获取读写锁用于写直接作差别. 读写锁的分 ...

  5. Linux 多线程互斥量互斥

    同步 同一个进程中的多个线程共享所在进程的内存资源,当多个线程在同一时刻同时访问同一种共享资源时,需要相互协调,以避免出现数据的不一致和覆盖等问题,线程之间的协调和通信的就叫做线程的同步问题, 线程同 ...

  6. 深刨显式锁ReentrantLock原理及其与内置锁的区别,以及读写锁ReentrantReadWriteLock使用场景

    13.显示锁 在Java5.0之前,在协调对共享对象的访问时可以使用的机制只有synchronized和volatile.Java5.0增加了一种新的机制:ReentrantLock.与之前提到过的机 ...

  7. Linux多线程实践(6) --Posix读写锁解决读者写者问题

    Posix读写锁 int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *rest ...

  8. UNIX环境高级编程——线程同步之读写锁以及属性

    读写锁和互斥量(互斥锁)很类似,是另一种线程同步机制,但不属于POSIX标准,可以用来同步同一进程中的各个线程.当然如果一个读写锁存放在多个进程共享的某个内存区中,那么还可以用来进行进程间的同步, 互 ...

  9. Java 中15种锁的介绍:公平锁,可重入锁,独享锁,互斥锁,乐观锁,分段锁,自旋锁等等

    Java 中15种锁的介绍 Java 中15种锁的介绍:公平锁,可重入锁,独享锁,互斥锁,乐观锁,分段锁,自旋锁等等,在读很多并发文章中,会提及各种各样锁如公平锁,乐观锁等等,这篇文章介绍各种锁的分类 ...

随机推荐

  1. 【LeetCode】204.计数质数

    问题描述: 统计所有小于非负整数 n 的质数的数量. 示例: 输入: 10 输出: 4 解释: 小于 10 的质数一共有 4 个, 它们是 2, 3, 5, 7 . 这是一道简单题,但是却并没有那么直 ...

  2. Hadoop 3.1.1 - Yarn 服务 - 总览

    YARN 服务 总览 Yarn 服务框架为在 Yarn 原生环境里长时间运行的服务,提供了一流的支持和接口.简言之,它扮演了容器编排系统的角色,统一管理 Yarn 上运行的容器化服务.它同时支持 Do ...

  3. 一口气说出 Redis 16 个常见使用场景!

    1.缓存 String类型 例如:热点数据缓存(例如报表.明星出轨),对象缓存.全页缓存.可以提升热点数据的访问数据. 文章首发于:http://ht5n8.cn/LEc6v 2.数据共享分布式 St ...

  4. VS2019 +MySQL+EntityFramework 使用配置与坑点避免随记

    一.安装运行环境 首先我们到mysql的官方网站上下载 mysql-installer-community-8.0.26.0 ,或者其他特定版本,通过它我们可以先将 mysql-for-visuals ...

  5. 【PTA|Python】浙大版《Python 程序设计》题目集:第二章

    前言 Hello!小伙伴! 非常感谢您阅读海轰的文章,倘若文中有错误的地方,欢迎您指出-   自我介绍 ଘ(੭ˊᵕˋ)੭ 昵称:海轰 标签:程序猿|C++选手|学生 简介:因C语言结识编程,随后转入计 ...

  6. MySQL数据库优化(2)

    MySQL优化 大批量插入数据优化 1.将数据按照id有序排列 2.使用load关键字(100万条:有序20s,无序1分50秒) 3.插入之前,关闭唯一性校验(SET UNIQUE_CHECKS=0) ...

  7. 【原创】oracle提权执行命令工具oracleShell v0.1

    帮一个兄弟渗透的过程中在内网搜集到了不少oracle连接密码,oracle这么一款强大的数据库,找了一圈发现没有一个方便的工具可以直接通过用户名密码来提权的.想起来自己之前写过一个oracle的连接工 ...

  8. Java MyEclipse:The type java.lang.CharSequence cannot be resolved. It is indirectly referen

    从svn上下载项目后配置weblogic后启动报错: myeclipse  The type java.lang.CharSequence cannot be resolved. It is indi ...

  9. ad room 拷贝

    当一个电路图中有多个相同组件的时候,我哦们一般使用 room格式拷贝. 基本思路 1.方法一  电路图使用repeat 多通道设计 2.简易的赋值粘贴,事实证明简单有效. 3.在pcb中删除对应一个单 ...

  10. SQL 练习4

    查询不存在" 01 "课程但存在" 02 "课程的情况 SELECT * from sc WHERE cid = '02' AND sid not in (SE ...