线程令牌桶的实现及条件变量改进版本

【互斥锁版本】

注意,若不满足到对某资源进行操作的条件,则一直while循环,反复轮询“开锁——等待——加锁”的过程,会造成“忙等”的现象。解决方法是通过条件变量的方式。会很占CPU的资源


struct mytbf_st
{
    
    
    int token;//积累的传输量
    int burst;//传输量的最大限制
    int cps;//每秒增加的传输量
    int pos;//在数组中下标的位置
    pthread_mutex_t mut;//我们需要对token进行操作 需要加锁
};

pthread_t tid_alarm;
struct mytbf_st * job[MYTBF_MAX];
pthread_mutex_t mutex_job = PTHREAD_MUTEX_INITIALIZER; 
//对job数组进行加锁
pthread_once_t once_init = PTHREAD_ONCE_INIT;
//对初始化操作只进行一次

//该函数对job数组进行了访问 但是没有加锁 因为在别的地方加锁后,才进行调用
int GetFreePos(void)
{
    
    
    int i;

    for(i = 0; i < MYTBF_MAX; i++)
    {
    
    
            if(NULL == job[i])
                    return i;
    }


    return -1;
}

//这是增加token的线程每1S都要进行的操作
void *AlarmHandler(void *p)
{
    
    
    int i;


    while(1)
    {
    
    
    	//先把job数组锁住
            pthread_mutex_lock(&mutex_job);

            for(i = 0; i < MYTBF_MAX; i++)
            {
    
    
                    if(NULL != job[i])
                    {
    
    
                    		//加锁对token的操作
                            pthread_mutex_lock(&job[i]->mut);

                            job[i]->token += job[i]->cps ;

                            if(job[i]->burst < job[i]->token)
                                    job[i]->token = job[i]->burst;
                            pthread_mutex_unlock(&job[i]->mut);
							//解锁对token的操作
                    }
            }
            pthread_mutex_unlock(&mutex_job);
			//解锁job数组
            sleep(1);
    }

}



void ModuleUnload(void)
{
    
    
    int i;

	//取消增加token的那个线程 并回收
    pthread_cancel(tid_alarm);
    pthread_join(tid_alarm, NULL);

	//销毁所有的令牌桶
    for(i = 0; i < MYTBF_MAX; i++)
    {
    
    
            if(NULL != job[i])
                     DestoryMytbf(job[i]);
    }

	//销毁锁
    pthread_mutex_destroy(&mutex_job);

}

void ModuleLoad(void)
{
    
    
    int err;

    err = pthread_create(&tid_alarm, NULL , AlarmHandler, NULL);
    if(err)
    {
    
    
            fprintf(stderr, "create() %s\n", strerror(err));
            exit(1);
    }


    atexit(ModuleUnload);
}


//这里返回一个void*
mytbf_st * InitMytbf(int nCps, int nBurst)
{
    
    
    struct mytbf_st *me;
    int nPos;
	
	//只会被执行一次
    pthread_once(&once_init, ModuleLoad);

    me = (struct mytbf_st *)malloc(sizeof(struct mytbf_st));
    if(NULL == me)
            return me;

    me->cps = nCps;
    me->token = 0;
    me->burst = nBurst;
    pthread_mutex_init(&me->mut, NULL);
	
	//对job数组的访问 进行加锁和解锁
    pthread_mutex_lock(&mutex_job);
    nPos = GetFreePos();
    if(-1 == nPos)
    {
    
    
            pthread_mutex_unlock(&mutex_job);
            free(me);
            return NULL;

    }

    me->pos = nPos;

    job[nPos] = me;
    pthread_mutex_unlock(&mutex_job);

    return me;

}

//从令牌桶中取出n个令牌 返回值是真正取到的令牌数
int TakeToken(mytbf_st *ptr , int nNum)
{
    
    
    struct mytbf_st *me = ptr;
    int min;

    if(nNum <= 0)
            return -1;

    pthread_mutex_lock(&me->mut);
    while(me->token <= 0)
    {
    
    
            pthread_mutex_unlock(&me->mut);
            sched_yield();
            pthread_mutex_lock(&me->mut);
    }

    min = nNum > me->token ? me->token : nNum;

    me->token -= min;
    pthread_mutex_unlock(&me->mut);

    return min;
}

//没有用完的令牌的回收
int ReturnToken(mytbf_st *ptr, int nNum)
{
    
    
    struct mytbf_st *me = ptr;
    int min;

    if(nNum <= 0)
            return -1;

    pthread_mutex_lock(&me->mut);

    me->token += nNum;
    if(me->token > me->burst)
            me->token = me->burst;

    pthread_mutex_unlock(&me->mut);


    return nNum;

}

//释放空间
int DestoryMytbf(mytbf_st *ptr)
{
    
    
    struct mytbf_st *me = ptr;

    pthread_mutex_lock(&mutex_job);
    job[me->pos] = NULL;
    pthread_mutex_unlock(&mutex_job);

    pthread_mutex_destroy(&me->mut);
    free(me);

    return 0;
}

改进版本

struct mytbf_st
{
    
    
        int token;
        int burst;
        int cps;
        int pos;
        pthread_mutex_t mut;
        pthread_cond_t cond;//加了一个条件变量
};

条件变量是线程的另外一种同步机制,这些同步对象为线程提供了会合的场所,理解起来就是两个(或者多个)线程需要碰头(或者说进行交互-一个线程给另外的一个或者多个线程发送消息),我们指定在条件变量这个地方发生,一个线程用于修改这个变量使其满足其它线程继续往下执行的条件,其它线程则接收条件已经发生改变的信号。

条件变量同锁一起使用使得线程可以以一种无竞争的方式等待任意条件的发生。所谓无竞争就是,条件改变这个信号会发送到所有等待这个信号的线程。而不是说一个线程接受到这个消息而其它线程就接收不到了。

void *AlarmHandler(void *p)
{
    
    
  int i;


  while(1)
  {
    
    
          pthread_mutex_lock(&mutex_job);

          for(i = 0; i < MYTBF_MAX; i++)
          {
    
    
                  if(NULL != job[i])
                  {
    
    
                          pthread_mutex_lock(&job[i]->mut);

                          job[i]->token += job[i]->cps ;

                          if(job[i]->burst < job[i]->token)
                                  job[i]->token = job[i]->burst;
							//此处改变了token 所以进行通知
                          pthread_cond_signal(&job[i]->cond);
                          pthread_mutex_unlock(&job[i]->mut);

                  }
          }
          pthread_mutex_unlock(&mutex_job);

          sleep(1);
  }

}

int TakeToken(mytbf_st *ptr , int nNum)
{
    
    
        struct mytbf_st *me = ptr;
        int min;

        if(nNum <= 0)
                return -1;

        pthread_mutex_lock(&me->mut);
        while(me->token <= 0)
        {
    
    
                //解锁等待cond_broadcast/cond_signal
                //在临界区外等待 被唤醒后 第一时间进行抢锁
                pthread_cond_wait(&me->cond, &me->mut);


                /*pthread_mutex_unlock(&me->mut);
                sched_yield();
                pthread_mutex_lock(&me->mut);*/
        }

        min = nNum > me->token ? me->token : nNum;

        me->token -= min;
        pthread_mutex_unlock(&me->mut);

        return min;
}
int ReturnToken(mytbf_st *ptr, int nNum)
{
    
    
        struct mytbf_st *me = ptr;
        int min;

        if(nNum <= 0)
                return -1;

        pthread_mutex_lock(&me->mut);

        me->token += nNum;
        if(me->token > me->burst)
                me->token = me->burst;
        //归还的token也会唤醒wait的进程
        pthread_cond_signal(&me->cond);

        pthread_mutex_unlock(&me->mut);


        return nNum;

}

条件变量,本身是由互斥量进行保护的。调用者把锁住的互斥量传给函数,函数自动把调用线程放到等待条件的线程列表上,并对互斥量解锁。当函数返回的时候,进行抢锁。

猜你喜欢

转载自blog.csdn.net/ZZHinclude/article/details/119715831