实现信号量(二) pipe实现信号量
来源:互联网 发布:mac safari开启无痕 编辑:程序博客网 时间:2024/04/30 05:02
上一篇开篇,实现了线程结构体。现在来开启模拟信号量吧。
本文是用pipe管道来实现信号量。
用管道实现信号量的原理是:当管道里没有内容时,调用read函数读,会造成线程的阻塞。用pv操作来描述的话,当用户调用p操作申请一个资源时,内部实现是调用read函数读取管道里的一个字符。如果能读取,就马上返回。否则阻塞线程。当用户调用v操作释放一个资源时,内部实现是往管道写入一个字符。
pipe_sem.hpp文件
#ifndef PIPE_SEM_HPP#define PIPE_SEM_HPP#include<pthread.h>typedef struct pipe_sem_tag{ int fd[2]; pthread_mutex_t mutex; int valid; //同线程结构体中的一样。}pipe_sem_t;//num是信号量的初始值int pipe_sem_init(pipe_sem_t* pip, int num);int pipe_sem_p(pipe_sem_t* pip);int pipe_sem_tryP(pipe_sem_t* pip);int pipe_sem_v(pipe_sem_t* pip);int pipe_sem_destroy(pipe_sem_t* pip);#endif // PIPE_SEM_HPP
pipe_sem.cpp文件
#include"pipe_sem.hpp"#include<sys/time.h>#include<sys/select.h>#include<errno.h>#include<unistd.h>#include<fcntl.h>#include<pthread.h>#define PIPE_SEM_VALID 0x78da//用管道实现信号量的原理是:当管道里没有内容时,调用read函数读,会造成线程的阻塞//用pv操作来描述的话,当用户调用p操作申请一个资源时,内部实现是调用read函数读取//管道里的一个字符。如果能读取,就马上返回。否则阻塞线程。//当用户调用v操作释放一个资源时,内部实现是往管道写入一个字符。//这个函数不是线程安全的。本系列文章中的所有init函数都不是线程安全的。//使用者应该在创建多个线程前就初始化好这些结构体。int pipe_sem_init(pipe_sem_t* pip, int num){ int status; if( pip == NULL || num < 0 ) return EINVAL; status = pipe(pip->fd); if( status != 0 ) return errno; status = pthread_mutex_init(&pip->mutex, NULL); if( status != 0 ) goto error;//通过向管道写入num个字符,模拟信号量的初始值为num while( num != 0 ) { status = write(pip->fd[1], "v", 1);; if( status != 1 ) goto error; --num; } pip->valid = PIPE_SEM_VALID; return 0; error: close(pip->fd[0]); //ignore the error close(pip->fd[1]); //ignore the error return status;}void cleanup_unlock(void* arg){ pthread_mutex_t *mutex = (pthread_mutex_t*)arg; pthread_mutex_unlock(mutex);}int pipe_sem_p(pipe_sem_t* pip){ static char ch; int status, rt; if( pip == NULL || pip->valid != PIPE_SEM_VALID) return EINVAL; //when one thread run the this function, another thread run the //pipe_sem_tryP, it will be wrong if don't use the mutex. //because pipe_sem_tryP function will change to fd[0] to non block status = pthread_mutex_lock(&pip->mutex); //得加锁 if( status != 0 ) return status; //因为read是可取消点。所以当线程在read中睡眠的时候, //其他线程调用thread_cancel取消这个睡眠的线程时, //睡眠的线程将苏醒,然后继续锁住mutex, 之后就退出终止。 //所以,要设定一个清理函数,发生这种情况时,在清理函数中解锁。 pthread_cleanup_push(cleanup_unlock, &pip->mutex); while( (status = read(pip->fd[0], &ch, 1) ) == -1 ) { if( errno == EINTR ) //被一个信号中断。遵循POSIX.1标准OS的不会这种情况 continue; else break; } if( status == - 1) rt = errno; else if( status == 0 ) //the end of pipe。即管道的写端被关闭了 rt = EPIPE; else rt = 0; pthread_cleanup_pop(0); pthread_mutex_unlock(&pip->mutex); //ignore the error return rt;}//通过fcntl函数把文件描述字fd[1]修改为非阻塞的。从而实现尝试功能。int pipe_sem_tryP(pipe_sem_t* pip){ int status, rt; int flags = 0; static char ch; if( pip == NULL || pip->valid != PIPE_SEM_VALID ) return EINVAL; //when one thread run the this function, another thread run the //pipe_sem_p, it will be wrong if don't use the mutex. //because this function will change to fd[0] to non block status = pthread_mutex_lock(&pip->mutex); if( status != 0 ) return status; pthread_cleanup_push(cleanup_unlock, &pip->mutex); if( (flags = fcntl(pip->fd[0], F_GETFL, 0)) < 0 ) { pthread_mutex_unlock(&pip->mutex); return errno; } flags |= O_NONBLOCK; //turn it to non block if( (flags = fcntl(pip->fd[0], F_SETFL, flags)) < 0 ) { pthread_mutex_unlock(&pip->mutex); return errno; } status = read(pip->fd[0], &ch, 1); if( status == 0 ) //the end of pipe rt = EPIPE; else if( status > 0 ) rt = 0; else rt = errno; pthread_cleanup_pop(0); error: flags &= ~O_NONBLOCK; //turn it to block fcntl(pip->fd[0], F_SETFL, flags); pthread_mutex_unlock(&pip->mutex); return rt;}int pipe_sem_v(pipe_sem_t* pip){ if( pip == NULL || pip->valid != PIPE_SEM_VALID) return EINVAL; return write(pip->fd[1], "v", 1);}int pipe_sem_destroy(pipe_sem_t* pip){ if( pip == NULL || pip->valid != PIPE_SEM_VALID) return EINVAL; close(pip->fd[0]); //ignore the error close(pip->fd[1]); //ignore the errorpip->valid = 0; return pthread_mutex_destroy(&pip->mutex);}
#include "pipe_sem.hpp"#include"Thread.hpp"#include <stdio.h>#include <string.h>#include <sys/types.h>#include <fcntl.h>#include <stdlib.h>#include <unistd.h>#include <errno.h>#defineNBUFF 8#define BUFFSIZE 4096struct {/* data shared by producer and consumer */ struct { chardata[BUFFSIZE];/* a buffer */ ssize_tn;/* count of #bytes in the buffer */ } buff[NBUFF];/* NBUFF of these buffers/counts */ pipe_sem_t nempty, nfull;/* semaphores, not pointers */ pipe_sem_t writer_mutex, reader_mutex;} shared;int writer_index = 0, reader_index = 0;int fd;/* input file to copy to stdout */void* produce(void *), *consume(void *);void* produce_tryP(void *arg);//运行测试程序时要输入一个文件名作为参数int main(int argc, char **argv){ Thread_t tid_produce1, tid_produce2, tid_produce3; Thread_t tid_consume1, tid_consume2; if (argc != 2) { printf("use <pathname> as pramater \n"); exit(1); } fd = open(argv[1], O_RDONLY); if( fd == -1 ) { printf("cann't open the file\n"); return -1; } pipe_sem_init(&shared.writer_mutex, 1); pipe_sem_init(&shared.reader_mutex, 1); pipe_sem_init(&shared.nempty, NBUFF); pipe_sem_init(&shared.nfull, 0); thread_init(&tid_produce1); thread_init(&tid_produce2); thread_init(&tid_produce3); thread_init(&tid_consume1); thread_init(&tid_consume2); thread_create(&tid_consume1, NULL, consume); thread_create(&tid_consume2, NULL, consume); thread_create(&tid_produce1, NULL, produce); thread_create(&tid_produce2, NULL, produce); thread_create(&tid_produce3, NULL, produce_tryP); thread_start(&tid_consume1, NULL); thread_start(&tid_consume2, NULL); thread_start(&tid_produce1, NULL); thread_start(&tid_produce2, NULL); thread_start(&tid_produce3, NULL); thread_join(&tid_consume1, NULL); thread_join(&tid_consume2, NULL); thread_join(&tid_produce1, NULL); thread_join(&tid_produce2, NULL); thread_join(&tid_produce3, NULL); thread_destroy(&tid_consume1); thread_destroy(&tid_consume2); thread_destroy(&tid_produce1); thread_destroy(&tid_produce2); thread_destroy(&tid_produce3);pipe_sem_destroy(&shared.writer_mutex);pipe_sem_destroy(&shared.reader_mutex); pipe_sem_destroy(&shared.nempty); pipe_sem_destroy(&shared.nfull); exit(0);}void *produce(void *arg){ while( 1 ) { pipe_sem_p(&shared.nempty);/* wait for at least 1 empty slot *///这个本来可以用互斥量的。不过为了多测试pipe_sem_t,就使用信号量了 pipe_sem_p(&shared.writer_mutex);//读取文件的内容 shared.buff[writer_index].n = read(fd, shared.buff[writer_index].data, BUFFSIZE);//文件的内容已经读取完毕。 if( shared.buff[writer_index].n == 0 ) {//离开前,要记得解锁 pipe_sem_v(&shared.nfull); pipe_sem_v(&shared.writer_mutex); return NULL; } writer_index = (writer_index+1)%NBUFF; //循环使用缓冲区 pipe_sem_v(&shared.nfull); pipe_sem_v(&shared.writer_mutex); } return NULL;}void* produce_tryP(void *arg){ int status; while( 1 ) { /* wait for at least 1 empty slot */ while( 1 ) { status = pipe_sem_tryP(&shared.nempty); if( status == 0 ) break; else if( status == EAGAIN ) { usleep(10*1000); //sleep 10 毫秒 continue; } else return NULL; } pipe_sem_p(&shared.writer_mutex); shared.buff[writer_index].n = read(fd, shared.buff[writer_index].data, BUFFSIZE); if( shared.buff[writer_index].n == 0 ) { pipe_sem_v(&shared.nfull); pipe_sem_v(&shared.writer_mutex); return NULL; } writer_index = (writer_index+1)%NBUFF; pipe_sem_v(&shared.nfull); pipe_sem_v(&shared.writer_mutex); } return NULL;}void* consume(void *arg){ while( 1 ) { pipe_sem_p(&shared.nfull); pipe_sem_p(&shared.reader_mutex);//缓冲区里面没有数据了。 if( shared.buff[reader_index].n == 0) {pipe_sem_v(&shared.nempty);pipe_sem_v(&shared.reader_mutex);return NULL;} write(STDOUT_FILENO, shared.buff[reader_index].data, shared.buff[reader_index].n); reader_index = (reader_index+1)%NBUFF; pipe_sem_v(&shared.nempty); pipe_sem_v(&shared.reader_mutex); } return NULL;}
测试结果:
0 0
- 实现信号量(二) pipe实现信号量
- 实现信号量(三) 消息队列实现信号量
- 实现信号量(四) 条件变量实现信号量
- 信号量的实现
- 信号量实现理发师问题
- java实现信号量
- java实现信号量
- 信号量实现同步
- 信号量实现读写锁
- Linux信号量的实现
- 使用Lock实现信号量
- java实现信号量
- 信号量实现的机制
- 实现信号量(一) 开篇
- 信号量的实现函数
- 信号量处理类实现
- 信号量实现进程同步
- 信号量实现线程同步
- sdfasdfaefg
- 心得-随想
- [ZZ] 编写你的第一个垃圾收集器
- 设计原则总提-1
- Qt中使用QAxObject操作Excel
- 实现信号量(二) pipe实现信号量
- 存储概念
- Java的内存机制
- Android学习进阶01——Activity
- 第一个OC程序
- 模式识别的比较好的总结。
- JDBC数据源(DataSource)的简单实现
- More Effective C++ 学习笔记
- Fastest Way to Update Rows in a Large Table in SQL Server