Unix下读者写者同步问题的模拟程序

p { margin-bottom: 0.21cm; }

/*

* ReaderWriter.c

* 题目 :2

* Unix 下读者写者同步问题的模拟程序 要求 4 个读进程 2 个写进程。

* 采用信号量方式实现。可以采用 Pthread 多线程编程。

*

* Created on: 2011-1-17

* Author: banxi1988

*

* 信号量通信机制主要用来实现进程间同步 , 信号 量值用来标识系统可用 资源的个数

* 例如可以使用信号号来标识一个缓冲区空间的大小 ,( 假定缓冲区空间大小为 256 个字节 .

* 在没有使用之前 , 该缓冲区没有任何内容 , 可用资源为 256, 即可以初始化信号 量为 256,

* 每向缓冲区写入一个字符 , 信号量的值自动减 1, 当信号量的值为 0 , 即表示缓冲区满 .

* 资源暂不可用 . 每从缓冲区读出一个字符 , 信号 时自动加 1, 如果信号量的值为 256, 则表示

* 缓冲区中没有内容 , 不可读 .

* 信号量初始化函数参考 :

* // Initialize semaphore object SEM to VALUE. If PSHARED then share it

// with other processes.

extern int sem_init (sem_t *__sem, int __pshared, unsigned int __value)

简单翻译如下 , 将信号量对象 SEM 初始化为 VALUE , 当信号量的值达到 PSHARED , 将其分享给其它进程 .

*/

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <pthread.h>

#include <time.h>

#include <semaphore.h>

 

#define WRITER 2 // 写者数量 . 2

#define READER 4 // 读者数量 4

#define BUFFER_SIZE 8 // 缓冲区 ( 大小 为 8 )

int writeOffset = 0; // 写者写内容的位置

int readOffset = 0; // 读者取内容的位置

 

int buffer[BUFFER_SIZE] = { '#' }; // '#' 字符代表空间没有内容

 

sem_t empty_sem; // 同步信号量, 当满了时阻止写者写 .

sem_t full_sem; // 同步信号量, 当没有内容时阻止读者读

pthread_mutex_t mutexlock; // 互斥锁, 一次只有一个线程访问缓冲

 

char letters[52] = { "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" };

 

 

/*

* 打印缓冲情况

*/

void printBuffer () {

int i;

printf ( "/t 当前缓冲区内容 :/t" );

for (i = 0; i < BUFFER_SIZE; i++)

printf ( "%c " , buffer[i]);

printf ( "/n" );

}

 

void writer ( int *id) {

srand (( unsigned ) time(NULL));

int index = 0;

while (1) {

sleep (1); // 先等待

//sem_wait( &empty_sem );// 等待缓冲区可写信号 .

pthread_mutex_lock (&mutexlock);

// 下面的条件语句为 if 时一位读写者 , 一次读写一个字母 . 改为 while 时则不同 .

if (buffer[writeOffset % BUFFER_SIZE] == '#' ) { // 如果有空间可写

 

index = rand () % 52;

writeOffset = writeOffset % BUFFER_SIZE;

buffer[writeOffset] = letters[index];

printf ( " %2d 位写者写了一个字母 %c /n" , *id,buffer[writeOffset]);

printBuffer();

++writeOffset;

} //WHILE

// 传递出 , 已经生产满的信号 . 然后解锁

//sem_post( &full_sem );

pthread_mutex_unlock (&mutexlock);

}

 

}

 

void reader ( int *id) {

 

while (1) {

sleep (1);

//sem_wait( &full_sem );// 等待写者写好的信号

pthread_mutex_lock (&mutexlock);

//

if (buffer[readOffset % BUFFER_SIZE] != '#' ) { // 有东西 可读

printf ( " %2d 位读者读出了一个字母 %c /n" , *id,buffer[readOffset]);

readOffset = readOffset % BUFFER_SIZE;

buffer[readOffset] = '#' ;

++readOffset;

}

//sem_post( &empty_sem );

pthread_mutex_unlock (&mutexlock);

}

 

}

 

int main () {

pthread_t writePID[WRITER]; //2

pthread_t readerPID[READER]; //4

int ret; // ret ~ return value

int i; //for loop

int wID[WRITER]; // 读者与写者的编号 , 因为线程的标识符太长了 , 不好看 .

int rID[READER];

 

/**

* 初始化同步信号量

*/

ret = sem_init (&empty_sem, 0, BUFFER_SIZE);

if (ret != 0) {

perror ( " sem init failed /n" );

exit (EXIT_FAILURE);

}

ret = sem_init (&full_sem, 0, BUFFER_SIZE);

if (ret != 0) {

perror ( " sem init failed /n" );

exit (EXIT_FAILURE);

}

 

/**

* 初始化互斥信号量

*/

ret = pthread_mutex_init (&mutexlock,NULL);

if (ret != 0) {

perror ( " mutexlock init failed /n" );

exit (EXIT_FAILURE);

}

 

/***

* 初始缓冲区的内容

*/

for (i = 0; i < BUFFER_SIZE;i++){

buffer[i] = '#' ;

}

 

printBuffer(); // 打印开始时候缓冲区的内容 :

/**

* 创建 WRITER(2) 个写者线程

*/

for (i = 0; i < WRITER; i++) {

wID[i] = i+1;

ret = pthread_create (&writePID[i], NULL, ( void *) *writer, &wID[i]);

if (ret != 0) {

perror ( "writer create failed /n" );

exit (EXIT_FAILURE);

}

}

 

/**

* 创建 READER(4) 个消费者线程

*/

for (i = 0; i < READER; i++) {

rID[i] = i+1;

ret = pthread_create (&readerPID[i], NULL, ( void *) *reader, &rID[i]);

if (ret != 0) {

perror ( "reader create failed /n" );

exit (EXIT_FAILURE);

}

}

 

/**

* 等待线程完成 .

*/

for (i = 0; i < WRITER; i++) {

pthread_join (writePID[i], NULL);

}

for (i = 0; i < READER; i++) {

pthread_join (readerPID[i], NULL);

}

 

exit (EXIT_SUCCESS);

}

 

/***

* 编译运行命令 :

* banxi @ banxi :~/ jeework / pthread $ gcc -g -o rwer ReaderWriter.c - lpthread

*

* 下面是运行结果及分析 : 总共有五不同次测试结果及分析 .

* banxi1@ banxi :~/ jeework / pthread $ ./ rwer

当前缓冲区内容 : # # # # # # # #

1 位写者写了一个字母 F

当前缓冲区内容 : F # # # # # # #

1 位读者读出了一个字母 F

2 位写者写了一个字母 n

当前缓冲区内容 : # n # # # # # #

2 位读者读出了一个字母 n

1 位写者写了一个字母 C

当前缓冲区内容 : # # C # # # # #

3 位读者读出了一个字母 C

2 位写者写了一个字母 X

当前缓冲区内容 : # # # X # # # #

4 位读者读出了一个字母 X

1 位写者写了一个字母 c

当前缓冲区内容 : # # # # c # # #

^C

banxi1988@banxi1988-desktop:~/ jeework / pthread $

( 说明 : 因为程序没有自己终止 , 所以用 CTRL+C 来中断 .)

ret = sem_init( &full_sem , 0, 0);

由于上面对于 full_sem 的初始值为 0, 终止值也是为 0,

所以出现 , 了读出一个写一个的状态 , 而不是写满之后再读完 .

一个读者一次读写一次的话 , 也是程序为了容易观察结果而设定的 . 因为判断是 if. 如果改成 while 就会不同了 .

将在下面对其进行测试 .

但把上面的改为如下的 :

ret = sem_init( &full_sem ,0,BUFFER_SIZE);

时程序运行结果如下 :

banxi1988@banxi1988-desktop:~/ jeework / pthread $ ./ rwer

当前缓冲区内容 : # # # # # # # #

1 写者写了一个字母 p

当前缓冲区内容 : p # # # # # # #

1 位读者读出了一个字母 p

2 写者写了一个字母 X

当前缓冲区内容 : # X # # # # # #

2 位读者读出了一个字母 X

1 写者写了一个字母 t

当前缓冲区内容 : # # t # # # # #

2 写者写了一个字母 t

当前缓冲区内容 : # # t t # # # #

4 位读者读出了一个字母 t

3 位读者读出了一个字母 t

1 写者写了一个字母 M

当前缓冲区内容 : # # # # M # # #

2 写者写了一个字母 J

当前缓冲区内容 : # # # # M J # #

2 位读者读出了一个字母 M

3 位读者读出了一个字母 J

1 写者写了一个字母 Y

当前缓冲区内容 : # # # # # # Y #

2 写者写了一个字母 m

 

测试结果三 : 将上面的条件设置 while 时的结果 :

 

banxi1988@banxi1988-desktop:~/ jeework / pthread $ ./ rwer

当前缓冲区内容 : # # # # # # # #

2 位写者写了一个字母 p

当前缓冲区内容 : p # # # # # # #

2 位写者写了一个字母 W

当前缓冲区内容 : p W # # # # # #

2 位写者写了一个字母 r

当前缓冲区内容 : p W r # # # # #

2 位写者写了一个字母 e

当前缓冲区内容 : p W r e # # # #

2 位写者写了一个字母 d

当前缓冲区内容 : p W r e d # # #

2 位写者写了一个字母 Z

当前缓冲区内容 : p W r e d Z # #

2 位写者写了一个字母 G

当前缓冲区内容 : p W r e d Z G #

2 位写者写了一个字母 s

当前缓冲区内容 : p W r e d Z G s

4 位读者读出了一个字母 p

4 位读者读出了一个字母 W

4 位读者读出了一个字母 r

4 位读者读出了一个字母 e

4 位读者读出了一个字母 d

4 位读者读出了一个字母 Z

4 位读者读出了一个字母 G

4 位读者读出了一个字母 s

1 位写者写了一个字母 e

当前缓冲区内容 : e # # # # # # #

1 位写者写了一个字母 F

当前缓冲区内容 : e F # # # # # #

1 位写者写了一个字母 E

当前缓冲区内容 : e F E # # # # #

1 位写者写了一个字母 N

当前缓冲区内容 : e F E N # # # #

1 位写者写了一个字母 I

当前缓冲区内容 : e F E N I # # #

1 位写者写了一个字母 A

当前缓冲区内容 : e F E N I A # #

1 位写者写了一个字母 Y

当前缓冲区内容 : e F E N I A Y #

1 位写者写了一个字母 U

当前缓冲区内容 : e F E N I A Y U

^C

banxi1988@banxi1988-desktop:~/ jeework / pthread $

 

运行结果四 : 去掉了待等待 相应信号量的代码 ..

这样的话读写的控制就又 if 或者 while 来判断了 .

这个一来 ,sleep 就必不可少 , 因为只有在一个线程 sleep 和时候 , 其它的线程才有抢占线程的平均的机会 .

为什么说平均呢 , 因为当写满或者读完的时候 , 线程也会释放掉锁的 .

但是马上又加入了抢点锁的过程 .

 

banxi1988@banxi1988-desktop:~/ jeework / pthread $ ./ sigwrer

当前缓冲区内容 : # # # # # # # #

1 位写者写了一个字母 s

当前缓冲区内容 : s # # # # # # #

2 位读者读出了一个字母 s

2 位写者写了一个字母 h

当前缓冲区内容 : # h # # # # # #

1 位读者读出了一个字母 h

1 位写者写了一个字母 Y

当前缓冲区内容 : # # Y # # # # #

2 位读者读出了一个字母 Y

2 位写者写了一个字母 I

当前缓冲区内容 : # # # I # # # #

1 位读者读出了一个字母 I

1 位写者写了一个字母 D

当前缓冲区内容 : # # # # D # # #

2 位读者读出了一个字母 D

2 位写者写了一个字母 Q

当前缓冲区内容 : # # # # # Q # #

3 位读者读出了一个字母 Q

1 位写者写了一个字母 c

当前缓冲区内容 : # # # # # # c #

2 位写者写了一个字母 z

^C

banxi1988@banxi1988-desktop:~/ jeework / pthread $

 

运行结果五 : 这个是将线程的信号量全都注释掉之后的代码 .

发现 , 读写纯种不再是交互执行的了 . 不过还是存在某些规律的 , 比如 , 先写的然后读的 . 这个也许是调度问题吧 ..

banxi1988@banxi1988-desktop:~/ jeework / pthread $ gcc -g -o sigwrer ReaderWriter.c - lpthread

banxi1988@banxi1988-desktop:~/ jeework / pthread $ ./ sigwrer

当前缓冲区内容 : # # # # # # # #

2 位写者写了一个字母 M

当前缓冲区内容 : M # # # # # # #

1 位写者写了一个字母 m

当前缓冲区内容 : M m # # # # # #

1 位读者读出了一个字母 M

2 位读者读出了一个字母 m

2 位写者写了一个字母 v

当前缓冲区内容 : # # v # # # # #

1 位写者写了一个字母 p

当前缓冲区内容 : # # v p # # # #

2 位读者读出了一个字母 v

1 位读者读出了一个字母 p

2 位写者写了一个字母 O

当前缓冲区内容 : # # # # O # # #

1 位写者写了一个字母 A

当前缓冲区内容 : # # # # O A # #

2 位读者读出了一个字母 O

1 位读者读出了一个字母 A

2 位写者写了一个字母 y

当前缓冲区内容 : # # # # # # y #

^C

banxi1988@banxi1988-desktop:~/ jeework / pthread $

*

*/

 

 

你可能感兴趣的:(JOIN,unix,Semaphore,测试,null,buffer)