前一篇讲了用消息队列实现信号量,这里使用条件变量实现信号量。有关条件变量的使用可以参考我的一篇博文。其实现原理和前面的两篇文章说述的有很大的不同。
其原理是通过一个变量sig_num来标明信号量的值(即资源的可用个数)。当然这个变量的修改要用一个mutex来锁住。当使用v操作释放一个资源时,在实现函数里面,将调用条件变量的信号发送函数pthread_cond_signal,唤醒线程,并且sig_num自加一标志可用资源多了一个。
还是上代码吧。
#ifndef COND_SEM_HPP
#define COND_SEM_HPP
#include"cond_sync.hpp"
#include
typedef Cond_sync_t cond_sem_t;
#define COND_SEM_INITIALIZER(num) COND_SYNC_INITIALIZER_V(num)
inline int cond_sem_init(cond_sem_t* con, int num)
{
int status = cond_sync_init(con);
con->sig_num = num;
return status;
}
inline int cond_sem_p(cond_sem_t* con)
{
return cond_sync_wait(con);
}
inline int cond_sem_tryP(cond_sem_t* con)
{
int status = cond_sync_timedwait(con, 0);
if( status == ETIMEDOUT )
return EAGAIN;
return status;
}
inline int cond_sem_v(cond_sem_t* con)
{
return cond_sync_signal(con);
}
inline int cond_sem_destroy(cond_sem_t* con)
{
return cond_sync_destroy(con);
}
#endif // COND_SEM_HPP
cond_sync.hpp文件
#ifndef COND_SYNC_HPP
#define COND_SYNC_HPP
#include
typedef struct Cond_sync_tag
{
pthread_mutex_t mutex;
pthread_cond_t cond;
int sig_num; //signal's num
int valid;
}Cond_sync_t;
#define COND_SYNC_VALID 0xabcd
#define COND_SYNC_INITIALIZER_V(num) \
{ PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, num, COND_SYNC_VALID}
#define COND_SYNC_INITIALIZER COND_SYNC_INITIALIZER_V(0)
int cond_sync_init(Cond_sync_t* cond_s);
int cond_sync_destroy(Cond_sync_t* cond_s);
int cond_sync_wait(Cond_sync_t* cond_s);
int cond_sync_timedwait(Cond_sync_t* cond_s, int msecs); //Millisecond
int cond_sync_signal(Cond_sync_t* cond_s);
#endif // COND_SYNC_HPP
#include"cond_sync.hpp"
#include
#include
int cond_sync_init(Cond_sync_t* cond_s)
{
int status;
status = pthread_mutex_init(&cond_s->mutex, NULL);
if( status != 0 )
goto error;
status = pthread_cond_init(&cond_s->cond, NULL);
if( status != 0 )
{
pthread_mutex_destroy(&cond_s->mutex);
goto error;
}
cond_s->sig_num = 0;
cond_s->valid = COND_SYNC_VALID;
return 0;
error:
return status;
}
int cond_sync_destroy(Cond_sync_t* cond_s)
{
int status1, status2;
if( cond_s == NULL || cond_s->valid != COND_SYNC_VALID )
return EINVAL;
cond_s->valid = 0;
//try best to destroy all object, so judge the status' after all
//object destroy
status1 = pthread_mutex_destroy(&cond_s->mutex);
status2 = pthread_cond_destroy(&cond_s->cond);
if( status1 != 0 )
return status1;
return status2;
}
void cleanup_unlock(void* arg)
{
pthread_mutex_t *mutex = (pthread_mutex_t*)arg;
pthread_mutex_unlock(mutex);
}
int cond_sync_wait(Cond_sync_t* cond_s)
{
int status;
if( cond_s == NULL || cond_s->valid != COND_SYNC_VALID )
return EINVAL;
status = pthread_mutex_lock(&cond_s->mutex);
if( status != 0 )
return status;
//因为pthread_cond_wait是可取消点。所以当线程在pthread_cond_wait
//中睡眠的时候,其他线程调用thread_cancel取消这个睡眠的线程时,
//睡眠的线程将苏醒,然后继续锁住mutex, 之后就退出终止。
//所以,要设定一个清理函数,发生这种情况时,在清理函数中解锁。
pthread_cleanup_push(cleanup_unlock, &cond_s->mutex);
while( cond_s->sig_num <= 0)
{
//cann't be interruptted by a signal
status = pthread_cond_wait(&cond_s->cond, &cond_s->mutex);
if( status != 0 )
{
break;
}
}
--cond_s->sig_num; //可用资源减一
pthread_cleanup_pop(0);
//ignore the error. if status == 0 and unlock return not 0.
//we cann't return this message to user. it will confuse the user
//the signal is sucessful, but return error code
pthread_mutex_unlock(&cond_s->mutex);
return status;
}
int cond_sync_timedwait(Cond_sync_t* cond_s, int msecs) //Millisecond
{
struct timeval now;
struct timespec waittime;
int status;
int sec;
if( cond_s == NULL || cond_s->valid != COND_SYNC_VALID )
return EINVAL;
if( msecs < 0 )
msecs = 0;
sec = msecs / 1000;
gettimeofday(&now, NULL);
waittime.tv_sec = now.tv_sec + sec;
waittime.tv_nsec = (now.tv_usec + (msecs%1000)*1000)*1000;
status = pthread_mutex_lock(&cond_s->mutex);
if( status != 0 )
return status;
pthread_cleanup_push(cleanup_unlock, &cond_s->mutex);
while( cond_s->sig_num <= 0 )
{
status = pthread_cond_timedwait(&cond_s->cond, &cond_s->mutex, &waittime);
if( status == EINTR ) //can be interruptted by a signal.
continue;
else
break;
}
if( status == 0 )
--cond_s->sig_num;
pthread_cleanup_pop(0);
//ignore the error. if status == 0 and unlock return not 0.
//we cann't return this message to user. it will confuse the user
//the signal is sucessful, but return error code
pthread_mutex_unlock(&cond_s->mutex);
return status;
}
int cond_sync_signal(Cond_sync_t* cond_s)
{
int status;
if( cond_s == NULL || cond_s->valid != COND_SYNC_VALID )
return EINVAL;
status = pthread_mutex_lock(&cond_s->mutex);
if( status != 0 )
goto error;
++cond_s->sig_num; //加一,表示可用资源多了一个
status = pthread_mutex_unlock(&cond_s->mutex);
if( status != 0 )
goto error;
status = pthread_cond_signal(&cond_s->cond);
error:
return status;
}
测试代码和前面的两个差不多。
#include "cond_sem.hpp"
#include"Thread.hpp"
#include
#include
#include
#include
#include
#include
#include
#define NBUFF 8
#define BUFFSIZE 4096
struct { /* data shared by producer and consumer */
struct {
char data[BUFFSIZE]; /* a buffer */
ssize_t n; /* count of #bytes in the buffer */
} buff[NBUFF]; /* NBUFF of these buffers/counts */
cond_sem_t nempty, nfull; /* semaphores, not pointers */
cond_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 as pramater \n");
exit(1);
}
fd = open(argv[1], O_RDONLY);
if( fd == -1 )
{
printf("cann't open the file\n");
return -1;
}
cond_sem_init(&shared.writer_mutex, 1);
cond_sem_init(&shared.reader_mutex, 1);
cond_sem_init(&shared.nempty, NBUFF);
cond_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);
cond_sem_destroy(&shared.writer_mutex);
cond_sem_destroy(&shared.reader_mutex);
cond_sem_destroy(&shared.nempty);
cond_sem_destroy(&shared.nfull);
exit(0);
}
void *produce(void *arg)
{
while( 1 )
{
cond_sem_p(&shared.nempty); /* wait for at least 1 empty slot */
cond_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 )
{
cond_sem_v(&shared.nfull);
cond_sem_v(&shared.writer_mutex);
return NULL;
}
writer_index = (writer_index+1)%NBUFF;
cond_sem_v(&shared.nfull);
cond_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 = cond_sem_tryP(&shared.nempty);
if( status == 0 )
break;
else if( status == EAGAIN )
{
usleep(10*1000); //sleep 10 毫秒
continue;
}
else
return NULL;
}
cond_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 )
{
cond_sem_v(&shared.nfull);
cond_sem_v(&shared.writer_mutex);
return NULL;
}
writer_index = (writer_index+1)%NBUFF;
cond_sem_v(&shared.nfull);
cond_sem_v(&shared.writer_mutex);
}
return NULL;
}
void* consume(void *arg)
{
while( 1 )
{
cond_sem_p(&shared.nfull);
cond_sem_p(&shared.reader_mutex);
if( shared.buff[reader_index].n == 0)
{
cond_sem_v(&shared.nempty);
cond_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;
cond_sem_v(&shared.nempty);
cond_sem_v(&shared.reader_mutex);
}
return NULL;
}
测试结果: