Golang编程语言知识介绍


  • 首页

  • todo

  • 思考

  • life

  • food

  • OS

  • lua

  • redis

  • Golang

  • C

  • TCP/IP

  • ebpf

  • p4

  • OpenVPN

  • IPSec

  • L2TP

  • DNS

  • distributed

  • web

  • OpenWRT

  • 运维

  • Git

  • 鸟哥的私房菜

  • IT杂谈

  • 投资

  • About Me

  • 友情链接

  • FTP

  • 搜索
close

操作系统基础16-读者写者问题

时间: 2021-08-24   |   分类: os   cs     |   阅读: 1942 字 ~4分钟

操作系统基础16-读者写者问题

2020-11-12 00:56·重学IT的老猫

进程同步问题是一个非常重要且相当有趣的问题,本篇我们对其中比较有名的读者-写者问题来进行学习。

操作系统基础16-读者写者问题

读者-写者

问题描述

假设一个数据库为多个并发进程所共享。有的进程可能只需要读数据库,而另一些进程可能更新(即读和写)数据库。为了区分这两种类型的进程,我们称前者为读者(Reader),称后者为写者(Writer)。显然,如果多个读者同时访问共享数据,而不会产生副作用。但如果某个写者和其他进程**(或读者或写者)**同时访问数据库时可能导致数据不一致的错误。

为了确保不会出现数据不一致的问题,要求写者在写入数据库时具有共享数据库独占的访问权限。这一同步问题称为读者-写者问题(reader-writer problem)。

所以读者-写者问题要求:

  1. 允许多个读者同时执行读操作;
  2. 不允许多个写者同时操作;
  3. 不允许读者、写者同时操作。

解决策略

读者-写者问题要解决: 读、读共享;写、写互斥;写、读互斥。

写者是比较简单的,它与任何线程互斥,用互斥信号量的 P操作、V 操作即可解决。

读者的问题比较复杂,它必须实现与写者的互斥,多个读者还可以同时读。仅仅简单的一对P操作、V操作是无法解决的。

可以有三种策略:读者优先、写者优先、读写公平。

读者优先

读进程只要看到有其他读进程正在访问文件,就可以继续作读访问;写进程必须等待所有读进程都不访问时才能写文件,即使写进程可能比一些读进程更早提出申请。可以使用一个计数器read_count记录读者总数目(包含等待和正在读的数目),如果read_count > 0 则写者等待,而读者直接读。当read_count = 0 写者与写者、写者与第一个读者抢占读写操作,这可以用一个二元信号量rw_mutex进行互斥访问。因为多个读者线程都要访问计数器,则使用一个二元信号量mutex进行互斥访问。

需要用到的共享变量:

int read_count = 0; // 系统当前读者进程数量
semaphore rw_mutex = 1; // 读者与写者互斥访问共享数据的互斥信号量 
semaphore mutex = 1;    // 多个读者进程互斥修改当前读者进程数量的信号量

写者进程结构

do {
	P(rw_mutex);
	...
	/* 修改共享数据 */
	...
	V(rw_mutex);
}while(true);

读者进程结构

do {
	P(mutex); //获取修改读者进程数量的互斥信号量,该操作在请求rw_mutex之前,防止出现死锁
	read_count++;
	if(read_count == 1)  //判断当前是否为第一个读者进程
		 P(rw_mutex);  //如果是就需要请求访问共享数据的互斥信号量
	V(mutex);  //read_count修改后 释放信号量
	...
	/* 读取数据 */
	...
	P(mutex);  //获取修改读者进程数量的互斥信号量
	read_count--;
	if(read_count == 0)  //判断当前进程是否为最后一个读者进程
		 V(rw_mutex);  //如果是则释放共享数据的互斥信号量,以允许写者进程操作共享数据
	V(mutex);
}while(true);

读者优先有可能导致写者进程产生饥饿现象,当系统中不断出现读者进程时,写者进程始终无法进入临界区。

写者优先

需要用到的共享变量:

int read_count = 0;         // 系统当前读者进程数量
int write_count = 0;        // 系统当前写者进程数量
semaphore rw_mutex = 1;     // 读者与写者互斥访问共享数据的互斥信号量
semaphore r_mutex = 1;      // 互斥修改当前读取文件的进程数
semaphore w_mutex = 1;      // 互斥修改当前修改文件的进程数
semaphore enter_mutex = 1;  // 获取申请访问文件的权限

写者进程结构

do {
    P(w_mutex);  //P操作 新的写者进程进入,上锁进行写者计数更新
    write_count++;   //更新写者计数器
	  if(write_count == 1)  // 判断当前是否为第一个写者进程
        P(enter_mutex); //P操作 则抢enter_mutex的锁,阻断后续到达的读者进程
	  V(w_mutex);         //V操作
    
    P(rw_mutex);  //获取访问文件的权限,文件可能被其它写者进程占用,或者等待最后一个读者进程释放
	    ...
	  /* 修改数据 */
	    ...
	  V(rw_mutex);
    P(w_mutex);
	  write_count--;
	  if(write_count == 0)  // 当所有写者进程都放弃使用文件时,运行读者进程申请访问文件
		  V(enter_mutex);
	  V(w_mutex);
}while(true);

读者进程结构

do {
    P(enter_mutex);  // 获取申请访问文件的权限 
    P(r_mutex);      //上锁进行读者计数更新
    read_count++;       //更新读者计数器
	  if(read_count == 1)  // 判断当前是否为第一个读者进程
        P(rw_mutex);  // 占用文件
	  V(r_mutex);
    V(enter_mutex); //
	    ...
	   /* 读取数据 */
	    ...
    P(r_mutex);
	  read_count--;
	  if(read_count == 0)
		   P(rw_mutex);   // 释放文件
	  P(r_mutex);
}while(true);

写者优先有可能导致读者进程产生饥饿现象,当系统中不断出现写者进程时,读者进程始终无法进入临界区。

读写公平

需要用到的共享变量:

int read_count = 0;         // 系统当前读者进程数量
semaphore rw_mutex = 1;     // 读者与写者互斥访问共享数据的互斥信号量
semaphore r_mutex = 1;      // 互斥修改当前读取文件的进程数
semaphore enter_mutex = 1;  // 获取申请访问文件的权限

写者进程结构

do { 
    P(enter_mutex); // 阻断后续到达的读者进程 
    P(rw_mutex); 
     ... 
    /* 修改数据 */ 
     ... 
    V(rw_mutex); 
    V(enter_mutex); 
}while(true);

读者进程结构

do {
    P(enter_mutex);  // 获取申请访问的权限,这里与写者优先的区别在于,写者放弃占用文件时,所有读者                            都可以与写者进程进行再次竞争 
    P(r_mutex);
    read_count++;
	  if(read_count == 1)  // 判断当前是否为第一个读者进程
        P(rw_mutex);
	  signal(r_mutex);
    signal(enter_mutex);  // 释放许可,其余读者和写者进程将进行下一轮竞争
	   ...
	   /* 读取数据 */
	   ...
    wait(r_mutex);
	  read_count--;
	  if(read_count == 0)
		   signal(rw_mutex);
	  signal(r_mutex);
}while(true);

上面的代码的 读者进程结构其实和第二个写者优先的 读者进程结构 是一样的,不一样的是写者进程结构。写者进程变成在每次写操作前都要等待 enter_mutex信号量。这里的P(enter_mutex) P操作并不会出现写者优先,而是按照先进先出的顺序让读者写者使用资源。

#os# #cs#
操作系统基础17-哲学家就餐问题
操作系统基础15-生产者消费者问题
shankusu2017@gmail.com

shankusu2017@gmail.com

日志
分类
标签
GitHub
© 2009 - 2025
粤ICP备2021068940号-1 粤公网安备44011302003059
0%