实现信号量(四) 条件变量实现信号量

         前一篇讲了用消息队列实现信号量,这里使用条件变量实现信号量。有关条件变量的使用可以参考我的一篇博文。其实现原理和前面的两篇文章说述的有很大的不同。

        其原理是通过一个变量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_sem_t是由Cond_sync_t实现的。

        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

        cond_sync.cpp文件

#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;
}


        测试结果:


你可能感兴趣的:(实现信号量)