读者与写者问题

***********************************************声明******************************************************

原创作品,出自 “晓风残月xj” 博客,欢迎转载,转载时请务必注明出处(http://blog.csdn.net/xiaofengcanyuexj)。

由于各种原因,可能存在诸多不足,欢迎斧正!

*********************************************************************************************************

在进行多线程编程的过程中,线程间的同步与互斥是件需要认真考虑的关键点,而读者与写者就是线程间同步的典型例子:若干个读者在读取文章,若干个写者同时编辑文章,保证多个读者和多个写者能并发或并行(关于并行与并发的区别以及多线程编程的一些基本概念,可以参考这篇博文:多线程初步)执行。解决读者与写着的典型方法是设立一个文章缓冲区,然后多个读者与写者互斥或共同地访问该缓冲区。 写操作是排他锁(排斥读者,同样排斥其他写者),读操作是共享锁(多个读者可读,排斥写者)。当然,当读者与写者、写者与写者几乎同一时间到达时,涉及到优先级的分配,此时也是应该考虑的问题。

1、模型分析

     读者与写者问题的特点在于读者不使缓冲区数量改变,并且多个读者可以同时读取缓冲区信息,而写者与写者、写者与读者只能互斥访问缓冲区的相同数据。但凡有写者的,可能会涉及读写冲突,即通常所说的数据竞争。对于相同的数据项,多个读者之间可以同时访问;对于相同的数据项,多个写者或写者与读者之间不能同时访问;对于不同的数据项,所有的读写访问是可以同时进行的。要尽可能提高效率,减少加锁范围,同时避免数据竞争引起的读写冲突。

贴段描述读者写着者问题模型的源代码,下面是对缓冲区的单个存储单元进行同步与互斥操作的。如果要对多个单元进行处理,可以讲int 型换成集合类型。

int count_Reader=0;
int mutex=1;
int WriterSemaphore=1;
Reader
{
	while(ture)
	{
		p( mutex );
		if( 0 == count_Reader )
			p( WriterSemaphore );
		++ count_Reader;
		v( mutex );

		进行读操作;

	    p( mutex );
		if( 1 == count_Reader )
			v( WriterSemaphore );
		-- count_Reader;
		v( mutex );
	}
}

Writer
{
	while(true)
	{
		p( WriterSemaphore );

		进行写操作

        v( WriterSemaphore );
	}
}

上面引入原子操作p、v操作,来实现同步与互斥。

2、源代码

由于对Java中较为底层的Lock、Condition等类不太了解,加之Java中比C++中关键代码段(CriticalSection)、事件(Event)封装更好,所以暂无法找到更为高效的方法,只能贴出调用ReadWriteLock接口的Java代码,还望大牛指点:

/**
 *    ReaderAndWriter.java
 *    Copyright 2014.11.6 XuJin
 **/
import java.util.List;
import java.util.ArrayList;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

class Artical {

	private static int m_nReaderCount = 0;
	private static int m_nWriterCount = 0;

	private ReadWriteLock lock = new ReentrantReadWriteLock();
	private Lock readLock = lock.readLock();
	private Lock writerLock = lock.writeLock();

	private String m_strName;
	private int m_nId;

	public Artical(String name, int id) {
		m_strName = name;
		m_nId = id;
	}

	public String getName() {
		readLock.lock();
		int seq = ++m_nReaderCount;
		System.out.println("第 " + seq + " 读者开始 ");
		String strRetValue;
		try {
			//读操作
			strRetValue = m_strName;
			System.out.println("   读者读取第 " + m_nId + " 个,内容为:" + strRetValue);
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		} finally {
			System.out.println("第 " + seq + " 读者结束 ");
			readLock.unlock();
		}
		return strRetValue;
	}

	public void setName(String name) {
		writerLock.lock();
		int seq = ++m_nWriterCount;
		System.out.println("第 " + seq + " 写者开始 ");
		try {
			// 写操作
			m_strName = name;
			System.out.println("   写者编辑第 " + m_nId + " 个,内容为:" + m_strName);
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}

		} finally {
			System.out.println("第 " + seq + " 写者结束 ");
			writerLock.unlock();
		}
	}
}

class ArticalBuffer {
	private List<Artical> m_ArrArticalBuffer;

	public ArticalBuffer() {
		m_ArrArticalBuffer = new ArrayList<Artical>();
	}

	public String getArtical(int id) {
		String retValue = null;
		if (id < m_ArrArticalBuffer.size())
			retValue = m_ArrArticalBuffer.get(id).getName();
		return retValue;
	}

	public void setArtical(int id, String name) {
		if (id < m_ArrArticalBuffer.size()) {
			{
				Artical art = m_ArrArticalBuffer.get(id);
				art.setName(name);
			}
		}
	}

	public void addArtical(String name) {
		m_ArrArticalBuffer.add(new Artical(name, m_ArrArticalBuffer.size()));
	}
}

class Reader extends Thread {
	ArticalBuffer m_ArticalBuffer;

	public Reader(ArticalBuffer pro) {
		m_ArticalBuffer = pro;
	}

	public void run() {
		int id = (int) (Math.random() * 4);
		m_ArticalBuffer.getArtical(id);
	}
}

class Writer extends Thread {

	ArticalBuffer m_ArticalBuffer;

	public Writer(ArticalBuffer pro) {
		m_ArticalBuffer = pro;
	}

	public void run() {
		int id = (int) (Math.random() * 4);
		String name = "XuJin";
		m_ArticalBuffer.setArtical(id, name);
	}
}

public class ReaderAndWriter {

	public static void main(String[] agrs) {
		String m_strName[] = { "Alice", "Bob", "Green", "Dell", "Marry", "Jim",
				"John", "Kobe", "James", "Wed", "Paul" };
		ArticalBuffer artBuffer = new ArticalBuffer();
		for (int i = 0; i < m_strName.length; ++i) {
			artBuffer.addArtical(m_strName[i]);
		}

		Reader[] readerSet = new Reader[20];
		for (int i = 0; i < 20; ++i) {
			readerSet[i] = new Reader(artBuffer);
			readerSet[i].start();
		}

		Writer[] writerSet = new Writer[5];
		for (int i = 0; i < 5; ++i) {
			writerSet[i] = new Writer(artBuffer);
			writerSet[i].start();
		}
	}
}

3、注意

1)、Lock替代synchronized关键字的使用,Condition 替代Object监视器方法(wait、notify 和 notifyAll)的使用;

2)、ReadWriteLock维护一对读写相关锁定,分别只读操作或写入操作。与互斥锁定相比,读-写锁定允许对共享数据进行更高级别的并发访问。虽然一次只有一个线程(writer 线程)可以修改共享数据,但在许多情况下,任何数量的线程可以同时读取共享数据(reader 线程),读-写锁定利用了这一点。从理论上讲,与互斥锁定相比,使用读-写锁定所允许的并发性增强将带来更大的性能提高。在实践中,只有在多处理器上并且只在访问模式适用于共享数据时,才能完全实现并发性增强。因此,与互斥锁定相比,使用读-写锁定能否提升性能则取决于读写操作期间读取数据相对于修改数据的频率,以及数据的争用——即在同一时间试图对该数据执行读取或写入操作的线程数。

由于时间有限,在写博文的过程中参考过一些文献,在此表示感谢;同时鉴于水平原因,你难免有不足之处,欢迎斧正!

时间: 2024-10-10 06:29:45

读者与写者问题的相关文章

Java 实现读者与写者

1 package r_d; 2 /** 3 * 以下读者与写者实验以swing为主要实现 4 */ 5 import java.awt.*; 6 import javax.swing.*; 7 import javax.swing.border.TitledBorder; 8 9 import java.awt.event.*; 10 import java.util.Random; 11 import java.util.concurrent.Semaphore; 12 import jav

多读者多写者

这个代码实现功能是:有写者在写时其他的写者不能再写,当写者全部写完时,读者才能继续读,当读者全部读完时写者才能继续写,写者每个循环写一次,读者每个循环读一次.#include<pthread.h> #include<string.h> #include<stdlib.h> #include<stdio.h> #include<semaphore.h> typedef struct { int value; sem_t sem,semw; int

读者与写者、生产者与消费者

1.读者写者 写者:写数据 读者:仅仅是读数据,不存在取走数据 写者与写者关系:互斥 读者与读者关系:没有关系 写者与读者关系:同步.互斥 解决方法: 1)读者优先:读者正在读,写者不能终止读者,直到读者读完,写者才能写入 2)写者优先:写者正在写,读者不能终止写者,直到写者写完,读者才能写入 2.生产者消费者 生产者:产生数据 消费者:取走数据 生产者与生产者:互斥关系 消费者与消费者:互斥关系 生产者与消费者:互斥.同步关系 生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程.与

进程同步——读者、写者问题

一个数据文件可以被多个进程所共享,把只要求读文件的进程成为“Reader进程”,其他进程称为“Writer进程”,允许多个进程读,但是不允许一个Writer进程和其他进程一起读或者写. 读者: 在读者问题中,把readcount当成了互斥信号量,在执行readcount减减或加加之前都要指向p操作. semaphore rmutex=1,wmutex=1; //rmutex信号量保证多个Reader进程对互斥资源的访问,这里把readcount当作了互斥资源 //wmutex信号量保证读写进程的

读者写者模型

编写多线程时有些共享的数据需要修改的机会少而读的机会多,在读的过程中消耗的时间较长,因在读的时候先要查找,因此加入读写锁对代码的访问较快,还很好的对代码块进行了加锁.读写锁实际是基于自旋锁的(当线程访问某资源条件不满足时,不挂起该线程,而是让它一直重复rwlock_trylock,适用于在锁中待得时间较短) 它也有三种关系(读者与读者之间无关系,写着与写着之间互斥,读者与写着之间同步.互斥),两个角色(读者.写着),一个场所 需要考虑读者与写着之间的优先级,避免读者一直读,而写着一直阻塞等待,没

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

Posix读写锁 int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr); int pthread_rwlock_destroy(pthread_rwlock_t *rwlock); int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); int pthread_rwlock_wrlock(pthre

秒杀多线程第十一篇 读者写者问题

与上一篇<秒杀多线程第十篇 生产者消费者问题>的生产者消费者问题一样,读者写者也是一个非常著名的同步问题.读者写者问题描述非常简单,有一个写者很多读者,多个读者可以同时读文件,但写者在写文件时不允许有读者在读文件,同样有读者在读文件时写者也不去能写文件. 上面是读者写者问题示意图,类似于生产者消费者问题的分析过程,首先来找找哪些是属于“等待”情况. 第一.写者要等到没有读者时才能去写文件. 第二.所有读者要等待写者完成写文件后才能去读文件. 找完“等待”情况后,再看看有没有要互斥访问的资源.由

使用读写锁解决读者-写者问题

读写锁 读写锁适合于对数据结构的读次数比写次数多得多的情况.因为,读模式锁定时可以共享,以写 模式锁住时意味着独占,所以读写锁又叫共享-独占锁. 初始化和销毁: #include <pthread.h> int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr); int pthread_rwlock_destroy(pthread_rwlock_t

读者写者问题

为什么会有自旋锁 在编写多线程的时候,有些公共数据读的概率远远大于修改的几率.通常而言,在读的过程中,往往伴随着查找的操作,中间耗时很长.给这种代码段加锁,会极大地降低我们程序的效率.我们引入了读写锁即自旋锁处理这种多读少写的情况. 2.什么是自旋锁 (1)它把对共享资源的访问者划分成读者和写者,读者只对共享资源进行读访问,写者则需要对共享资源进行写操作. (2)这种锁相对于自旋锁而言,能提高并发性. (3)在多处理器系统中: a.对于读者:它允许同时有多个读者来访问共享资源,最大可能的读者数为