您的位置:首页 > 运维架构 > Linux

Linux 关于读者与写者同步互斥问题的解析

2016-11-28 20:03 981 查看
      在操作系统课程中,读者与写者问题(或是称作生产者与消费者问题),一直是同步与互斥问题的经典例子,在大学的操作系统课程中也是作为经典例子进行讲解,今天我们就来解析一下读者与写者互斥同步关系。

      首先要做的是先了解一下问题的内容,首先需要肯定的是先要有写者将文件进行编辑,即书写内容,然后才会有读者进行阅读,这个是符合我们基本的常识的,进而开始分析这个比较令人脑热的复杂关系。

     先从写者开始,写者需要的限制条件比较明显:

                       写者与任何人都是互斥关系,若有读者在进行读操作时,要等待其完成后才可以进行操作;若有写者进行操作时,也需要等待。

因此我们需要一个互斥变量进行限制读者和写者的操作,仅允许一个线程可以进行操作,要完成该操作我们需要调用pthread_mutex_t writez (包含在头文件<pthread.h>)作为我们的互斥信号量,该信号量同时用于读者线程中,这样,写者与写者互斥,写着与读者互斥,同时争用一个信号量writez,一旦有一个线程争得,就将这个信号量锁定,则其他线程必须等待其用完后将信号量释放才可以继续操作。同时可以增加线程的功能,真正实现写者的写功能,调用函数ssize_t
write(int fd, const void *buf, size_t count)(包含在头文件<unistd.h>)

参数:   
fd:要进行写操作的文件描述词。
buf:需要输出的缓冲区
count:最大输出字节计数

还需要调用open()函数对文件进行操作(函数原型:int
open(const char *pathname,int flags,int perms))

//所需头文件:#include <sys/types.h>,#include
<sys/stat.h>,#include <fcntl.h>

以及read()函数

所需头文件: #include <unistd.h>

函数原型:ssize_t read(int fd, void *buf, size_t count);

参数:  
fd: 将要读取数据的文件描述词。
buf:指缓冲区,即读取的数据会被放到这个缓冲区中去。
count: 表示调用一次read操作,应该读多少数量的字符。

从而实现写的功能,但此过程中我们仍需要一个互斥信号量进行对过程的互斥限制,在此我们先埋下一个伏笔,这在很多程序中都难免会遗漏的一个具危险的buger。

    在开始对读者的分析:

             读者的限制比较多,首先我们还需要强调一个关键点,那就是第一个读者的问题,众所周知,只有第一个读者需要与写者竞争互斥信号量,因为一旦第一个读者竞争到资源后,后续的读者会同步的使用资源,而无需判断是否有写者在操作,因此需要对第一个读者进行标记,这就是在程序开头我们进行强调的readert = 1作为是否是第一个读者的判断依据,在此,必须进行互斥控制,让多个线程竞争成为第一个读者。这样第二个信号量产生pthread_mutex_t
readz。若是第一个读者先将readert--,清除标记,好为下一次的第一个读者使用,调用pthread_mutex_lock(&writez),进行互斥竞争,若竞争成功,就锁定信号量,但无需释放信号量,此后,所有的读者都尅同步的进行读操作,直到最后一个读者结束,因此,还需要一个readll作为对读者数量的统计,但必须是互斥进行操作,否则多个线程同时进行时,上一个进行"+"操作,而下一个进行"-"操作,则这个readll就失去意义了。因此必须只有一个线程使用readll,则第三个互斥信号量产生pthread_mutex_t
kz,作为控制对读者个数的操作,最重要的是在readll == 0 时,即最后一个读者结束后要释放信号量,以供写者竞争。

     经过梳理我们现在拥有三个信号量,基本上已经可以完成对读者与写者问题的程序分析,但编写完程序后发现无论如何都无法控制写者的写操作,总是现出来提示可以输入内容时,又有读者进行读操作,这就是我们在开始留下的伏笔,因为在整个过程中,我们都没有对文件的操作进行控制,导致在写者进行写操作时读者依旧可以对文件进行操作,因此最后一个互斥信号量pthread_mutex_t file产生,控制对文件的操作。
//readert = 1 作为是否是第一个读者

#include<stdio.h>

#include<string.h>

#include<unistd.h>

#include<pthread.h>

#include<sys/types.h>

#include<sys/stat.h>

#include<fcntl.h>

#define BUFFER_SIZE 128        //最大字符数
#define SRCV "srcv.txt"               //文件名
#define OFFSET 0
int number = 0;                            //写者标号
char ch = 'A';                               //读者标号
int real_read_len = 0;
int readert = 0;
int flag_t = 0;                               //作为是否有内容可读,=1表示有内容可读
int readll = 0;                               //作为统计读者个数的
pthread_mutex_t writez;
pthread_mutex_t readz;
pthread_mutex_t file;
pthread_mutex_t kz;
void destroy(void)                            //信号量消除
{
    pthread_mutex_destroy(&writez);
    pthread_mutex_destroy(&readz);
    pthread_mutex_destroy(&file);
    pthread_mutex_destroy(&kz);
}
/////////////////////////////////////////////////////////////////////////////
void *readerl(void *p)                          //读者
{
   char num = (char) p;
   char src_buff[BUFFER_SIZE];
   int src;

if(readll == 0)                                 //若无其他读者就设下一个读者为第一个
pthread_mutex_lock(&kz);
  readert++;
pthread_mutex_unlock(&kz);
  if(readert == 1)                            //如果是第一个读者需要判断是否有写者运行
{
  pthread_mutex_lock(&writez);    //互斥开始
pthread_mutex_lock(&kz);
readert = 0;
pthread_mutex_unlock(&kz);
if(flag_t == 0)                                //判断有无内容可读
{
 printf("NO.%c:No text to read...process is exiting..\n",num);
 return ;
}
pthread_mutex_lock(&readz);
   readll++;                                       //读者个数++
pthread_mutex_unlock(&readz);
  printf("NO.%c reader is reading(This is the first reader Now)\n",num);
pthread_mutex_lock(&file);
  src = open(SRCV,O_RDONLY,S_IRUSR|S_IWUSR|S_IRGRP,S_IROTH);//作为只读文件
  if(src < 0)
  {
    printf("OPen the file Error!\n");
    return ;
  }
pthread_mutex_unlock(&file);
   lseek(src,OFFSET,SEEK_SET);  //将指针指向文件起始位置
   real_read_len = 1;
while(real_read_len > 0)
   real_read_len = read(src,src_buff,sizeof(src_buff));//读文件
   printf("NO.%c reader reads the text:%s\n",num,src_buff);
//usleep(5000);
   printf("NO.%c reader reads finish\n",num);
pthread_mutex_lock(&readz);
   readll--;
pthread_mutex_unlock(&readz);
return ;
}
/////////////////////////////////////////////////////////////////////////////////
 else  //非第一个读者
{
pthread_mutex_lock(&readz);
  readll++;
pthread_mutex_unlock(&readz);

pthread_mutex_lock(&file);
  printf("NO.%c reader is reading\n",num);
 // printf("NOW  the Readll = %d\n",readll);
  src = open(SRCV,O_RDONLY,S_IRUSR|S_IWUSR|S_IRGRP,S_IROTH);//作为只读文件
  if(src < 0)
  {
    printf("Open the file Error!\n");
    return ;
  }

   lseek(src,OFFSET,SEEK_SET);          //将指针指向文件起始位置
   real_read_len = 1;
   real_read_len = read(src,src_buff,sizeof(src_buff));//读文件
   printf("NO.%c reader reads the text:%s\n",num,src_buff);
   printf("NO.%c reader finish\n",num);
pthread_mutex_unlock(&file);

pthread_mutex_lock(&readz);
   readll--;
pthread_mutex_unlock(&readz);
//printf("NOW the Readll = %d;the readert = %d\n",readll,readert);
 if(readll == 0){

     pthread_mutex_unlock(&writez);}
}
return ;
}
//////////////////////////////////////////////////////////////////////////////////
void * writerl(void * p)
{
  long int num = (long int)p;
   char dest_buff[BUFFER_SIZE];
   int  dest;

pthread_mutex_lock(&writez);                     //写者对所有的都互斥
printf("NO.%ld writer is writing\n",num);
pthread_mutex_lock(&file);

   dest = open(SRCV,O_WRONLY|O_TRUNC|O_CREAT,S_IRUSR|S_IWUSR|S_IRGRP,S_IROTH);//以只写方式打开,若文件已经存在,那么会删除文件中的全部原有数据,并且设置文件大小为0
if(dest < 0)
{
  printf("Open file Error\n");
  return ;
}

  flag_t = 1;
  printf("Please input message(< 512)\n");
  scanf("%s",dest_buff);
  write(dest,dest_buff,sizeof(dest_buff));

pthread_mutex_unlock(&file);
  printf("NO.%ld writer writing finish\n",num);
pthread_mutex_unlock(&writez);
return ;
}
///////////////////////////////////////////////////////////////
void *moon(void *p)
{
pthread_t we,re;
 int ad,cd;
while(1){
printf("Please input num of writer\n");
scanf("%d",&ad);
printf("Please input num of reader (end : 0)\n");
scanf("%d",&cd);
if(cd == 0)
{
printf("Please wait ,the process is exiting\n");
sleep(3);
break;
return ;
}
int i;
for(i = 1; i <= ad; i++)
{
pthread_create(&we,NULL,writerl,(void *)(++number));}

int j;
for(j = 1; j <= cd; j++){
pthread_create(&re,NULL,readerl,(void *)(++ch));}
sleep(10);
}
destroy();
return ;
}
//////////////////////////////////////////////
int main()
{
pthread_t pt;
pthread_create(&pt,NULL,moon,NULL);
pthread_join(pt,NULL);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息