自旋锁(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. 【C#】C#中使用GDAL3(一):Windows下超详细编译C#版GDAL3.3.0(VS2015+.NET 4+32位/64位)

    转载请注明原文地址:https://www.cnblogs.com/litou/p/15004877.html 目录 一.介绍 二.编译准备 三.编译SQLite 四.编译LibTiff 五.编译PR ...

  2. POJ1934 Trip 题解

    LCS 模板,但要输出具体方案,这就很毒瘤了. 神奇的预处理:fa[i][j]表示在 \(a\) 串的前 \(i\) 个字符中,字母表第 \(j\) 个字母最晚出现的位置,fb[i][j]同理. 这样 ...

  3. Mysql的登录

    一.mysql申请连接的四种方式 1 . TCP/IP TCP/IP套接字连接方式是MySQL在任何平台都提供的一种连接方式,也是网络中使用最多的一种方式.这种方式在TCP/IP连接上建立一个基于网络 ...

  4. 有语言基础的人应该如何学习python?

    正好最近在学python,感觉有语言基础的话更多在乎一些语法糖,毕竟其他东西在之前应该接触过了. 笔者C++是起始语言,也接触过java.js,介绍一点python的特点吧.帮助自己巩固所学,也希望能 ...

  5. PS Lite 笔记

    本文讲解的 PS Lite 源码版本限定如下: GitHub: https://github.com/dmlc/ps-lite/tree/master Commit: f45e2e78a7430be0 ...

  6. git合作开发流程

    一.创建项目与管理 创建项目和管理项目都是管理账号需要做的事情,如果只是合作开发不进行管理,只需要浏览第二部分的内容即可. 1.创建项目 登录代码托管网站,点击添加项目,如下图所示: 填写相应的项目信 ...

  7. WinUI 3学习笔记(2)—— 用ListView来展示集合

    在WPF的时代,我们多是使用ListBox和ListView来展示,纵向滚动条显示的集合数据.这两个控件的默认样式,以及对触控的支持,已完全落后于时代.他们两个分别长这样,和Win10及Win11的风 ...

  8. Jetpack Compose 1.0 终于要投入使用了!

    前言 Jetpack Compose 是用于构建原生界面的「新款 Android 工具包」.2021 Google IO 大会上,Google宣布:「Jetpack Compose 1.0 即将面世」 ...

  9. AI中各种浮点精度概念集合:fp16,fp32,bf16,tf32,fp24,pxr24,ef32

    常见的浮点类型有fp16,fp32,bf16,tf32,fp24,pxr24,ef32,能表达的数据范围主要看exponent,精度主要看fraction. 可以看出表达的数据范围看fp32,bf16 ...

  10. 从跨域与同源策略谈CSRF防御与绕过

    之前偶然看到群里有小伙汁问这个token相关的问题,当时我酝酿了一下子,没想好怎么总结,今天来说一下 CSRF在过去还属于OWASP TOP10 ,现在已经不是了(补充一点:关于OWASP API 请 ...