读写锁、自旋锁

概述:

  在一些程序中存在读者写者问题,也就是说,对某些资源的访问会  存在两种可能的情况,一种是访问必须是排它行的,就是独占的意思,这称作写操作;

另一种情况就是访问方式可以是共享的,就是说可以有多个线程同时去访问某个资源,这种就称作读操作。这个问题模型是从对文件的读写操作中引申出来的。
  读写锁比起mutex具有更高的适用性,具有更高的并行性,可以有多个线程同时占用读模式的读写锁,但是只能有一个线程占用写模式的读写锁,读写锁的三种状态:
  1.当读写锁是写加锁状态时,在这个锁被解锁之前,所有试图对这个锁加锁的线程都会被阻塞
  2.当读写锁在读加锁状态时,所有试图以读模式对它进行加锁的线程都可以得到访问权,但是以写模式对它进行加锁的线程将会被阻塞
  3.当读写锁在读模式的锁状态时,如果有另外的线程试图以写模式加锁,读写锁通常会阻塞随后的读模式锁的请求,这样可以避免读模式锁长期占用,而等待的写模式锁请求则长期阻塞。
  读写锁最适用于对数据结构的读操作次数多于写操作的场合,因为,读模式锁定时可以共享,而写模式锁定时只能某个线程独占资源,因而,读写锁也可以叫做个共享-独占锁。
  处理读者-写者问题的两种常见策略是强读者同步(strong reader synchronization)和强写者同步(strong writer synchronization).    在强读者同步中,总是给读者更高的优先权,只要写者当前没有进行写操作,

  读者就可以获得访问权限;而在强写者同步中,则往往将优先权交付给写者,而读者只能等到所有正在等待的或者是正在执行的写者结束以后才能执行。关于读者-写者模型中,

  由于读者往往会要求查看最新的信息记录,所以航班订票系统往往会使用强写者同步策略,而图书馆查阅系统则采用强读者同步策略。
  读写锁机制是由posix提供的,如果写者没有持有读写锁,那么所有的读者多可以持有这把锁,而一旦有某个写者阻塞在上锁的时候,那么就由posix系统来决定是否允许读者获取该锁。

  在创建互斥锁的时候有两种方式,①:定义一个全局变量pthread_rwlock_t。②利用malloc进行动态分配。切记不可定义成局部变量,读写锁是要被多个线程访问的,

  如果读写锁是被定义在一个线程内部的局部变量,当出了作用域被另一个线程访问时,这个锁就变成了无效的,还有可能引起无法预料的后果。

基本操作函数

  1.初始化和销毁读写锁
  对于读写锁变量的初始化可以有两种方式,一种是通过给一个静态分配的读写锁赋予常值PTHREAD_RWLOCK_INITIALIZER来初始化它,另一种方法就是通过调用pthread_rwlock_init()来动态的初始化。

  而当某个线程不再需要读写锁的时候,可以通过调用     

  pthread_rwlock_destroy来销毁该锁。函数原型如下:
  #include 
  int pthread_rwlock_init(pthread_rwlock_t *rwptr, const pthread_rwlockattr_t *attr);
  int pthread_rwlock_destroy(pthread_rwlock_t *rwptr);
  这两个函数如果执行成功均返回0,如果出错则返回错误码。
  在释放某个读写锁占用的内存之前,要先通过pthread_rwlock_destroy对读写锁进行清理,释放由pthread_rwlock_init所分配的资源。
  在初始化某个读写锁的时候,如果属性指针attr是个空指针的话,表示默认的属性;如果想要使用非默认属性,则要使用到下面的两个函数:
  #include 
  int pthread_rwlockattr_init(pthread_rwlockattr_t *attr);
  int pthread_rwlockattr_destroy(pthread_rwlockatttr_t *attr);
  这两个函数同样的,如果执行成功返回0,失败返回错误码。
  这里还需要说明的是,当初始化读写锁完毕以后呢,该锁就处于一个非锁定状态。
  数据类型为pthread_rwlockattr_t的某个属性对象一旦初始化了,就可以通过不同的函数调用来启用或者是禁用某个特定的属性。

  2.获取和释放读写锁
  读写锁的数据类型是pthread_rwlock_t,如果这个数据类型中的某个变量是静态分配的,那么可以通过给它赋予常值PTHREAD_RWLOCK_INITIALIZAR来初始化它。pthread_rwlock_rdlock()用来获取读出锁,

  如果相应的读出锁已经被某个写入者占有,那么就阻塞调用线程。pthread_rwlock_wrlock()用来获取一个写入锁,如果相应的写入锁已经被其它写入者或者一个或多个读出者占有,那么就阻塞该调用线程;

  pthread_rwlock_unlock()用来释放一个读出或者写入锁。函数原型如下:
  #include 
  int pthread_rwlock_rdlock(pthread_rwlock_t *rwptr);
  int pthread_rwlock_wrlock(pthread_rwlock_t *rwptr);
  int pthread_rwlock_unlock(pthread_rwlock_t *rwptr);
  这三个函数若调用成功则返回0,失败就返回错误码。要注意的是其中获取锁的两个函数的操作都是阻塞操作,也就是说获取不到锁的话,那么调用线程不是立即返回,而是阻塞执行。有写情况下,

  这种阻塞式的获取所得方式可能不是很适用,所以,接下来引入两个采用非阻塞方式获取读写锁的函数pthread_rwlock_tryrdlock()和pthread_rwlock_trywrlock(),非阻塞方式下获取锁的时候,如果不能马上获取到,

  就会立即返回一个EBUSY错误,而不是把调用线程投入到睡眠等待。函数原型如下:
  #include 
  int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwptr);
  int pthread_rwlock_trywrlock(pthread_rwlock_t *rwptr);
  同样地,这两个函数调用成功返回0,失败返回错误码。

参考代码

#include <stdio.h>
#include <pthread.h>

pthread_rwlock_t lock;
int data;

void *thread_write()
{
        while(1)
        {
                usleep(50000);
                if(pthread_rwlock_wrlock(&lock) == 0)
                {
                        data++;
                        printf("writer a num: %d\n",data);
                        pthread_rwlock_unlock(&lock);
                }
                else
                {
                        printf("@\[email protected]\n");
                }
        }
}

void *thread_read()
{
        while(1)
        {
                sleep(1);
                if(pthread_rwlock_rdlock(&lock) == 0)
                {
                        printf("readr a num: %d\n",data);
                        pthread_rwlock_unlock(&lock);
                }
                else
                {
                        printf("@\[email protected]\n");
                }
        }
}

int main(void)
{
        pthread_rwlock_init(&lock,NULL);

        pthread_t reader,writer;

        pthread_create(&reader,NULL,thread_read,NULL);
        pthread_create(&writer,NULL,thread_write,NULL);

        pthread_join(reader,NULL);
        pthread_join(writer,NULL);

        pthread_rwlock_destroy(&lock);

        return 0;
}

参考:http://blog.chinaunix.net/uid-27177626-id-3791049.html

     http://blog.csdn.net/KOwzb/article/details/72869352?locationNum=2&fps=1

     http://www.cnblogs.com/Anker/archive/2013/01/09/2853137.html(重点阅读)

   http://www.cnblogs.com/huangwei/archive/2010/05/19/1739659.html(重点阅读)

时间: 2024-10-09 13:50:10

读写锁、自旋锁的相关文章

线程学习--(十三)重写锁、读写锁、锁的高级深化

一.Concurrent.util常用类 1.CountDownLacth使用: 他经常用于监听某些初始化操作,等初始化执行完毕后,通知主线程继续工作 package thread3; import java.util.concurrent.CountDownLatch; public class UseCountDownLatch { public static void main(String[] args) { final CountDownLatch countDown = new Co

C# lock 语法糖实现原理--《.NET Core 底层入门》之自旋锁,互斥锁,混合锁,读写锁

原文:C# lock 语法糖实现原理--<.NET Core 底层入门>之自旋锁,互斥锁,混合锁,读写锁 在多线程环境中,多个线程可能会同时访问同一个资源,为了避免访问发生冲突,可以根据访问的复杂程度采取不同的措施 原子操作适用于简单的单个操作,无锁算法适用于相对简单的一连串操作,而线程锁适用于复杂的一连串操作 原子操作 修改状态要么成功且状态改变,要么失败且状态不变,并且外部只能观察到修改前或者修改后的状态,修改中途的状态不能被观察到 .NET 中,System.Threading.Inte

自旋锁&amp;读/写锁

自旋锁 自旋锁(spin lock)是用来在多处理器环境中工作的一种特殊的锁.如果内核控制路径发现自旋锁"开着",就获取锁并继续自己的执行.相反,如果内核控制路径发现由运行在另一个CPU上的内核控制路径"锁着",就在一直循环等待,反复执行一条紧凑的循环指令,直到锁被释放. 一般来说,由自旋锁所保护的每个临界区都是禁止内核抢占的.在单处理器系统上,这种锁本身并不起锁的作用,自旋锁原语仅仅是禁止或启用内核抢占.请注意,在自旋锁忙等期间,内核抢占还是有效的,因此,等待自旋

进程读写锁和信号量

进程读写锁 Pthread_rwlockattr_setshared, 当将process-shared属性设为Pthread_process_shared时,可以得到一个进程间的读写锁,如果process-shared属性设为pthread_process_private, 那么能操作这个读写锁的线程只能是创建这个读写锁的进程中的线程, pthread_shared的默认值为pthread_process_private int __pthread_rwlock_rdlock (rwlock)

Linux 同步方法剖析--内核原子,自旋锁和相互排斥锁

在学习 Linux® 的过程中,您或许接触过并发(concurrency).临界段(critical section)和锁定,可是怎样在内核中使用这些概念呢?本文讨论了 2.6 版内核中可用的锁定机制,包含原子运算符(atomic operator).自旋锁(spinlock).读/写锁(reader/writer lock)和内核信号量(kernel semaphore). 本文还探讨了每种机制最适合应用到哪些地方.以构建安全高效的内核代码. 本文讨论了 Linux 内核中可用的大量同步或锁定

linux内核同步之每CPU变量、原子操作、内存屏障、自旋锁【转】

转自:http://blog.csdn.net/goodluckwhh/article/details/9005585 版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[-] 一每CPU变量 二原子操作 三优化和内存屏障 四自旋锁 自旋锁 自旋锁的数据结构和宏函数 读写自旋锁 读写自旋锁的相关函数 linux内核中的各种“任务”都能看到内核地址空间,因而它们之间也需要同步和互斥.linux内核支持的同步/互斥手段包括: 技术 功能 作用范围 每CPU变量 为每个CPU复制一份数据

Linux 同步方法剖析--内核原子,自旋锁和互斥锁

在学习 Linux® 的过程中,您也许接触过并发(concurrency).临界段(critical section)和锁定,但是如何在内核中使用这些概念呢?本文讨论了 2.6 版内核中可用的锁定机制,包括原子运算符(atomic operator).自旋锁(spinlock).读/写锁(reader/writer lock)和内核信号量(kernel semaphore). 本文还探讨了每种机制最适合应用到哪些地方,以构建安全高效的内核代码. 本文讨论了 Linux 内核中可用的大量同步或锁定

【转】自旋锁及其衍生锁

原文网址:http://blog.chinaunix.net/uid-26126915-id-3032644.html 自旋锁 自旋锁(spinlock)是用在多个CPU系统中的锁机制,当一个CPU正访问自旋锁保护的临界区时,临界区将被锁上,其他需要访问此临界区的CPU只能忙等待,直到前面的CPU已访问完临界区,将临界区开锁.自旋锁上锁后让等待线程进行忙等待而不是睡眠阻塞,而信号量是让等待线程睡眠阻塞.自旋锁的忙等待浪费了处理器的时间,但时间通常很短,在1毫秒以下. 自旋锁用于多个CPU系统中,

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

Java 中15种锁的介绍 在读很多并发文章中,会提及各种各样锁如公平锁,乐观锁等等,这篇文章介绍各种锁的分类.介绍的内容如下: 公平锁 / 非公平锁 可重入锁 / 不可重入锁 独享锁 / 共享锁 互斥锁 / 读写锁 乐观锁 / 悲观锁 分段锁 偏向锁 / 轻量级锁 / 重量级锁 自旋锁 上面是很多锁的名词,这些分类并不是全是指锁的状态,有的指锁的特性,有的指锁的设计,下面总结的内容是对每个锁的名词进行一定的解释. 公平锁 / 非公平锁 公平锁 公平锁是指多个线程按照申请锁的顺序来获取锁. 非公

比较synchronized和读写锁

一.科普定义 这篇博文的两个主角“synchronized”和“读写锁” 1)synchronized 这个同步关键字相信大家都用得比较多,在上一篇“多个线程之间共享数据的方式”中也详细列举他的应用,在这就不多说只做几点归纳: Java提供这个关键字,为防止资源冲突提供的内置支持.当任务执行到被synchronized保护的代码片段的时候,它检查锁是否可用,然后获取锁,执行代码,释放锁. 常用这个关键字可以修饰成员方法和代码块 2)读写锁 我们对数据的操作无非两种:“读”和“写”,试想一个这样的