• linux高编线程-------线程同步-条件变量


    条件变量的使用:将互斥量的忙等机制改为通知机制

    涉及到的函数有以下几个:

    int pthread_cond_destroy(pthread_cond_t *cond);
    /**********************
     *功能:条件变量的初始化
     *参数:cond:条件变量
     *      attr:条件变量的属性
     * ********************/
    int pthread_cond_init(pthread_cond_t *restrict cond , const pthread_condattr_t *restrict attr);
    /***静态初始化***/
    pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
    
    /*****发送广播给所有处于阻塞等待状态的线程********/
    int pthread_cond_broadcast(pthread_cond_t *cond);
    /******叫醒某一个处于阻塞等待状态的线程*******/
    int pthread_cond_signal(pthread_cond_t *cond);
    
    
    int pthread_cond_timedwait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex,const struct timespec *restrict abstime);
    /********使线程阻塞在一个条件变量上********/
    int pthread_cond_wait(pthread_cond_t *restrict cond , pthread_mutex_t *restrict mutex);

     eg:

     mytbf.c

    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <errno.h>
    #include <pthread.h>
    
    #include "mytbf.h"
    
    //每个令牌桶内部的信息状态
    struct mytbf_st
    {
        int cps;//速率
        int burst;//上限
        int token;//令牌个数
        int pos;//令牌桶在这个令牌桶数组中的位置下标
        pthread_mutex_t mut;//并发多个线程同时使用成员,保证变量被独占使用,所以每个令牌桶应该有一把锁
        pthread_cond_t cond;//通知token累加完成
    };
    
    //令牌桶数组
    static struct mytbf_st *job[MYTBF_MAX];
    //由于令牌桶数组可能被其他并发线程同时访问修改(eg:同时调用init),所以需要添加互斥量
    static pthread_mutex_t mut_job = PTHREAD_MUTEX_INITIALIZER;
    //线程
    static pthread_t tid;
    //只执行一次
    static pthread_once_t init_once = PTHREAD_ONCE_INIT;
    
    //线程:任务是负责每秒向令牌桶数组中加token
    static void *thr_alrm(void *p)
    {
        int i;
    
        while(1)
        {
            //令牌桶数组加锁
            pthread_mutex_lock(&mut_job);
            for(i = 0 ; i < MYTBF_MAX; i++)
            {
                if(job[i] != NULL)
                {
                    //锁令牌并自加
                    pthread_mutex_lock(&job[i]->mut);
                    //加令牌
                    job[i]->token += job[i]->cps;
                    if(job[i]->token > job[i]->burst)
                        job[i]->token = job[i]->burst;
                    pthread_cond_broadcast(&job[i]->cond);
                    pthread_mutex_unlock(&job[i]->mut);
                }
            }
            //令牌桶解锁
            pthread_mutex_unlock(&mut_job);
            sleep(1);
        }
    }
    
    //模块的卸载
    static void module_unload(void)
    {
        int i;
        //取消线程以及收尸
        pthread_cancel(tid);
        pthread_join(tid,NULL);
        //将令牌桶释放空间并销毁
        pthread_mutex_lock(&mut_job);
        for(i = 0 ; i < MYTBF_MAX ;i++)
        {
            if(job[i] != NULL)
            {
                pthread_mutex_destroy(&job[i]->mut);
                pthread_cond_destroy(&job[i]->cond);
                free(job[i]);
            }
    
        }
        pthread_mutex_unlock(&mut_job);
        pthread_mutex_destroy(&mut_job);
    
        return ;
    }
    //模块加载
    static void module_load(void)
    {
        int err;
        //创建线程
        err = pthread_create(&tid,NULL,thr_alrm,NULL);
        if(err)
        {
            fprintf(stderr,"pthread_create():%s
    ",strerror(err));
            exit(1);
        }
        //挂载钩子函数:当exit时,调用module_unload
        atexit(module_unload);
    }
    
    
    //获取空置的令牌数组位置
    static int get_free_pos_unlocked(void)
    {
        int i;
    
        for(i = 0 ; i < MYTBF_MAX; i++)
            if(job[i] == NULL)
                return i;
        return -1;
    }
    
    
    //令牌桶初始化
    mytbf_t *mytbf_init(int cps,int burst)
    {
        struct mytbf_st *me;
        int pos;
        //只调用一次module_load
        pthread_once(&init_once,module_load);
        //申请空间
        me = malloc(sizeof(*me));
        if(me == NULL)
            return NULL;
        //赋初值
        me->cps = cps;
        me->burst = burst;
        me->token = 0;
        pthread_mutex_init(&me->mut,NULL);
        pthread_cond_init(&me->cond,NULL);
    
        //令牌桶加锁查看空位并赋值
        pthread_mutex_lock(&mut_job);
    
        pos = get_free_pos_unlocked();
        if(pos < 0)
        {
            free(me);
            pthread_mutex_unlock(&mut_job);
            return NULL;
        }
    
        me->pos = pos;
        job[pos] = me;
    
        pthread_mutex_unlock(&mut_job);
    
        return me;
    }
    
    static int min(int a,int b)
    {
        if(a < b)
            return a;
        return b;
    }
    //获取令牌个数
    int mytbf_fetchtoken(mytbf_t *ptr,int size)
    {
        struct mytbf_st *me = ptr;
        int n;
    
        if(size < 0)
            return -EINVAL;
        //锁令牌并处理
        pthread_mutex_lock(&me->mut);
        //没有令牌,解决忙等
        while(me->token <= 0)
        {
            //非忙等
            pthread_cond_wait(&me->cond,&me->mut);
            /*忙等
            pthread_mutex_lock(&me->mut);
            sched_yield();
            pthread_mutex_unlock(&me->mut);
            */
        }
    
        n = min(me->token,size);
    
        me->token -= n;
    
        pthread_mutex_unlock(&me->mut);
    
        return n;
    }
    
    //返还令牌
    int mytbf_returntoken(mytbf_t *ptr,int size)
    {
        struct mytbf_st *me = ptr;
    
        if(size < 0)
            return -EINVAL;
        //加锁处理令牌
        pthread_mutex_lock(&me->mut);
    
        me->token += size;
        if(me->token > me->burst)
            me->token = me->burst;
        //令牌归还后广播
        pthread_cond_broadcast(&me->cond);
        pthread_mutex_unlock(&me->mut);
    
        return 0;
    }
    //销毁令牌
    int mytbf_destroy(mytbf_t *ptr)
    {
        struct mytbf_st *me = ptr;
        //令牌桶加锁销毁
        pthread_mutex_lock(&mut_job);
        job[me->pos] = NULL;
        pthread_mutex_unlock(&mut_job);
        //销毁互斥量和条件变量
        pthread_mutex_destroy(&me->mut);
        pthread_cond_destroy(&me->cond);
    
        free(ptr);
        return 0;
    }

    mytbf.h

     + main.c  makefile  + mytbf.c  mytbf.h                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               X
    #ifndef MYTBF_H__
    #define MYTBF_H__
    
    typedef void mytbf_t;
    
    #define MYTBF_MAX       1024
    
    mytbf_t *mytbf_init(int cps,int burst);
    
    int mytbf_fetchtoken(mytbf_t *,int size);
    
    int mytbf_returntoken(mytbf_t *,int size);
    
    int mytbf_destroy(mytbf_t *);
    
    
    #endif

    main.c

    /******************
     *功能:按照流控方式(正常每秒10个字节,有令牌桶机制)从文件中读取数据并写stdout
     *      使用多线程方式
     *      使用互斥量和条件变量
     * ***************/
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <errno.h>
    #include <signal.h>
    #include <string.h>
    
    #include "mytbf.h"
    //速率:每秒传输10个字节
    #define CPS         10
    #define BUFSIZE     1024
    //令牌上限:100个
    #define BURST       100
    
    int main(int argc,char **argv)
    {
        int sfd,dfd = 1;
        char buf[BUFSIZE];
        int pos,len,ret;
        int size;
        mytbf_t *tbf;
        //1.判断输入文件路径是否正确合法
        if(argc < 2)
        {
            fprintf(stderr,"Usage...
    ");
            exit(1);
        }
        //2.初始化令牌桶:设置速率和令牌上限
        tbf = mytbf_init(CPS,BURST);
        if(tbf == NULL)
        {
            fprintf(stderr,"mytbf_init() failed.
    ");
            exit(1);
        }
        //3.尝试打开文件
        do
        {
            sfd = open(argv[1],O_RDONLY);
            if(sfd < 0)
            {
                if(errno != EINTR)
                {
                    perror("open()");
                    exit(1);
                }
            }
        }while(sfd < 0);
    
        //4.读写文件
        while(1)
        {
            //4.1获取令牌个数
            size = mytbf_fetchtoken(tbf,BUFSIZE);
            if(size < 0)
            {
                fprintf(stderr,"mytbf_fetchtoken():%s
    ",strerror(-size));
                exit(1);
            }
            //4.2读取相应个数的数据
            while((len = read(sfd,buf,size)) < 0)
            {
                if(errno == EINTR)
                    continue;
                perror("read()");
                break;
            }
            //4.3无数据返回
            if(len == 0)
                break;
    
            //4.4有数据判断文件实际读取的长度是否与令牌个数相同
            //   如果令牌数量 >  实际读取数量 需要返还令牌
            if(size-len > 0)
                mytbf_returntoken(tbf,size-len);
    
            //4.5输出位置定向
            pos = 0;
            //4.6写到输出文件
            while(len > 0)
            {
                ret = write(dfd,buf+pos,len);
                if(ret < 0)
                {
                    if(errno == EINTR)
                        continue;
                    perror("write()");
                    exit(1);
                }
                pos += ret;
                len -= ret;
            }
        }
        //5.关闭文件,并销毁令牌桶
        close(sfd);
    
        mytbf_destroy(tbf);
    
        exit(0);
    }

    Makefile:

    CFLAGS+=-pthread
    LDFLAGS+=-pthread
    
    all:mytbf
    
    mytbf:mytbf.o main.o
        gcc $^ -o $@ $(CFLAGS) $(LDFLAGS)
    
    clean:
        rm -rf *.o mytbf
  • 相关阅读:
    event与WaitForSingleObject、MsgWaitForMultipleObjects等
    vc不包含MFC就不打印内存泄露?
    使用visual leak detector(vld)查找内存泄露
    C#里面中将字符串转为变量名
    如何编写nopCommerce插件
    object成员,不见了!
    NopCommerce 定制系列(一):增加 Sha256+Base64 加密
    c#中的二维数组与锯齿数组
    待搞清楚
    NopCommerce 2.5的部署
  • 原文地址:https://www.cnblogs.com/muzihuan/p/5358975.html
Copyright © 2020-2023  润新知