使用epoll+时间堆实现高性能定时器

在开发Linux网络程序时,通常需要维护多个定时器,如维护客户端心跳时间、检查多个数据包的超时重传等。如果采用Linux的SIGALARM信号实现,则会带来较大的系统开销,且不便于管理。

本文在应用层实现了一个基于时间堆的高性能定时器,同时考虑到定时的粒度问题,由于通过alarm系统调用设置的SIGALARM信号只能以秒为单位触发,因此需要采用其它手段实现更细粒度的定时操作,当然,这里不考虑使用多线程+sleep的实现方法,理由性能太低。

通常的做法还有采用基于升序的时间链表,但升序时间链表的插入操作效率较低,需要遍历链表。因此本实现方案使用最小堆来维护多个定时器,插入O(logn)、删除O(1)、查找O(1)的效率较高。

首先是每个定时器的定义:

class heap_timer
{
public:
    heap_timer( int ms_delay )
    {
        gettimeofday( &expire, NULL );
        expire.tv_usec += ms_delay * 1000;
        if ( expire.tv_usec > 1000000 )
        {
            expire.tv_sec += expire.tv_usec / 1000000;
            expire.tv_usec %= 1000000;
        }
    }

public:
    struct timeval expire;
    void (*cb_func)( client_data* );
    client_data* user_data;
    ~heap_timer()
    {
        delete user_data;
    }
};

包括一个超时时间expire、超时回调函数cb_func以及一个user_data变量,user_data用于存储与定时器相关的用户数据,用户数据可以根据不同的应用场合进行修改,这里实现的是一个智能博物馆的网关,网关接收来自zigbee协调器的用户数据,并为每个用户维护一段等待时间T,在T到来之前,同一个用户的所有数据都存放到user_data的target_list中,当T到来时,根据target_list列表选择一个适当的target并发送到ip_address,同时删除定时器(有点扯远了=。=)。总之,要实现的功能就是给每个用户维护一个定时器,定时值到来时做一些操作。

class client_data
{
public:
    client_data(char *address):target_count(0)
    {
        strcpy(ip_address,address);
    }
private:
    char ip_address[32];
    target target_list[64];
    int target_count;
    ......
};

以下是时间堆的类定义,包括了一些基本的堆操作:插入、删除、扩容,还包括了定时器溢出时的操作函数tick()

class time_heap
{
public:
    time_heap( int cap  = 1) throw ( std::exception )
        : capacity( cap ), cur_size( 0 )
    {
        array = new heap_timer* [capacity];
        if ( ! array )
        {
            throw std::exception();
        }
        for( int i = 0; i < capacity; ++i )
        {
            array[i] = NULL;
        }
    }

    ~time_heap()
    {
        for ( int i =  0; i < cur_size; ++i )
        {
            delete array[i];
        }
        delete [] array;
    }

public:
    int get_cursize()
    {
        return cur_size;
    }

    void add_timer( heap_timer* timer ) throw ( std::exception )
    {
        if( !timer )
        {
            return;
        }
        if( cur_size >= capacity )
        {
            resize();
        }
        int hole = cur_size++;
        int parent = 0;
        for( ; hole > 0; hole=parent )
        {
            parent = (hole-1)/2;
            if ( timercmp( &(array[parent]->expire), &(timer->expire), <= ) )
            {
                break;
            }
            array[hole] = array[parent];
        }
        array[hole] = timer;
    }
    void del_timer( heap_timer* timer )
    {
        if( !timer )
        {
            return;
        }
        // lazy delelte
        timer->cb_func = NULL;
    }
    int top(struct timeval &time_top) const
    {
        if ( empty() )
        {
            return 0;
        }
        time_top = array[0]->expire;
        return 1;
    }
    void pop_timer()
    {
        if( empty() )
        {
            return;
        }
        if( array[0] )
        {
            delete array[0];
            array[0] = array[--cur_size];
            percolate_down( 0 );
        }
    }
    void tick()
    {
        heap_timer* tmp = array[0];
        struct timeval cur;
        gettimeofday( &cur, NULL );
        while( !empty() )
        {
            if( !tmp )
            {
                break;
            }
            if( timercmp( &cur, &(tmp->expire), < ) )
            {
                break;
            }
            if( array[0]->cb_func )
            {
                array[0]->cb_func( array[0]->user_data );
            }
            pop_timer();
            tmp = array[0];
        }
    }
    bool empty() const
    {
        return cur_size == 0;
    }
    heap_timer** get_heap_array()
    {
        return array;
    }

private:
    void percolate_down( int hole )
    {
        heap_timer* temp = array[hole];
        int child = 0;
        for ( ; ((hole*2+1) <= (cur_size-1)); hole=child )
        {
            child = hole*2+1;
            if ( (child < (cur_size-1)) && timercmp( &(array[child+1]->expire), &(array[child]->expire), < ) )
            {
                ++child;
            }
            if ( timercmp( &(array[child]->expire), &(temp->expire), < ) )
            {
                array[hole] = array[child];
            }
            else
            {
                break;
            }
        }
        array[hole] = temp;
    }
    void resize() throw ( std::exception )
    {
        heap_timer** temp = new heap_timer* [2*capacity];
        for( int i = 0; i < 2*capacity; ++i )
        {
            temp[i] = NULL;
        }
        if ( ! temp )
        {
            throw std::exception();
        }
        capacity = 2*capacity;
        for ( int i = 0; i < cur_size; ++i )
        {
            temp[i] = array[i];
        }
        delete [] array;
        array = temp;
    }


private:
    heap_timer** array;
    int capacity;
    int cur_size;
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/464137.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring JDBC最佳实践(2)

2019独角兽企业重金招聘Python工程师标准>>> 使用DataSourceUtils进行Connection的管理 由上节代码可知&#xff0c;JdbcTemplate在获取Connection的时候&#xff0c;并不是直接调用DataSource的getConnection(),而是调用了如下的代码&#xff1a; Connection con …

我所感受到的上海

大家好&#xff0c;文章转自张老师的公众号&#xff0c;文章的我不是小编本人&#xff0c;小编现居深圳&#xff0c;刚接受了一场大雨的洗礼。前两天公众号抽奖的书籍已经发货&#xff0c;中奖的朋友们注意查收。当格蠹园里的大灰反复犹豫到底应该在哪里生产的时候&#xff0c;…

时间操作(Java版)—获取给定日期N天后的日期

版权声明&#xff1a;本文为博主原创文章。未经博主同意不得转载。https://blog.csdn.net/wangshuxuncom/article/details/34896777 获取给定日期N天后的日期import java.util.Calendar;public class Test {public static void main(String[] args) {System.out.println(new Te…

设计模式学习笔记五——Prototype模式

动机&#xff1a;使用原型实例指定创建对象的种类&#xff0c;然后通过拷贝这些原型来创建新的对象。某些结构复杂对象面临着剧烈变化&#xff0c;但拥有比较稳定一致的接口&#xff0c;如何隔离出这些易变对象&#xff0c;使客户程序不随之改变&#xff1f;场景&#xff1a;Th…

2011年最佳代码

2019独角兽企业重金招聘Python工程师标准>>> 2011年最佳代码 try { if(you.bevieve(it) true || you.believe(it) false) { I.believe(it); } } catch(Exception ex) { throw new Exception("Its a miracle!"); } finally { it.justHappened(); } 转载于…

使用Epoll 在 Linux 上开发高性能应用服务器

epoll是Linux提供一种多路复用的技术&#xff0c;类似各个平台都支持的select&#xff0c;只是epoll在内核的实现做了更多地优化&#xff0c;可以支持比select更多的文件描述符&#xff0c;当然也支持 socket这种网络的文件描述符。Linux上的大并发的接入服务器&#xff0c;目前…

昨晚两点睡

深圳下雨两天&#xff0c;我们居家办公两天&#xff0c;不过奇怪的事情是&#xff0c;这两天我都到公司上班&#xff0c;昨天早上没有下雨&#xff0c;我想着应该要去公司&#xff0c;结果到了公司才知道原来今天可以居家办公。不过&#xff0c;在公司才有上班的感觉&#xff0…

hashmap详解

一.hashmap的数据结构 HashMap采取数组加链表的存储方式(哈希表)来实现。亦即数组&#xff08;散列桶&#xff09;中的每一个元素都是链表 二.hashmap的构造函数 HashMap()&#xff1a;构造一个具有默认初始容量 (16) 和默认加载因子 (0.75) 的空 HashMap。 HashMap(int initia…

最近忙,三个字

最近忙&#xff0c;三个字 转载于:https://www.cnblogs.com/Liunsh/archive/2007/06/13/782352.html

书籍推荐-记这几年看的书

这几年看了不少书&#xff0c;大部分是自掏腰包&#xff0c;看一本好书是享受&#xff0c;我很喜欢这种感觉。 这些是我这几年看书的一些心得&#xff0c;对于一些新手来说&#xff0c;可能有点帮助。 这几年一直在走技术路线&#xff0c;所以看的大部分都是技术方面的书籍&…

不复位MCU直接调试运行程序,让bug闻风丧胆

大家周末好呀&#xff0c;文章转自bug菌的公众号&#xff0c;文章介绍步复位情况下调试bug&#xff0c;希望对大家有用。1调试窘境经常有朋友在开发中遇到这样的窘境&#xff0c;当单片机程序运行异常以后&#xff0c;由于调试信息做得并不是很全面&#xff0c;导致相应的问题场…

数据库设计中的14个技巧

数据库设计中的14个技巧 1. 原始单据与实体之间的关系   可以是一对一、一对多、多对多的关系。在一般情况下&#xff0c;它们是一对一的关系&#xff1a;即一张原始单据对 应且只对应一个实体。在特殊情况下&#xff0c;它们可能是一对多或多对一的关系&#xff0c;即一张原…

游三圣乡山中湖岛有感

游三圣乡山中湖岛有感——代腾飞 2007年6月10日 于成都池塘水绿萍飘荡柳絮随风四飞扬独坐湖亭把歌唱夕阳西下断愁肠 转载于:https://www.cnblogs.com/daitengfei/archive/2007/06/26/795914.html

这次比opencv快⑥倍!!!

打败opencv ,哦&#xff0c;是快了3倍上回书说道&#xff0c;我用汇编neon实现去畸变算法比opencv快3倍&#xff0c;这都不算啥&#xff0c;这次新增了透视变换算法&#xff0c;二者加起来比opencv快6倍&#xff01;拭目以待吧。啥玩意是透视变换&#xff1f;相信你们都开过高级…

数据和数据类型

一、什么是数据&#xff1a; 数据(date)是事实或观察的结果&#xff0c;是对客观事物的逻辑归纳&#xff0c;是用于表示客观事物的未加工的原始素材。 1&#xff09;数据是信息的表现形式和载体&#xff0c;可以是符号、文字、数字、语音、图像、视频等。数据和信息是不可分离…

TCP/UDP优化设置总结以及MTU的相关介绍

TCP/IP协议涉及到四层&#xff0c;从底层到上层异常为&#xff1a;链路层&#xff0c;网络层&#xff0c;传输层&#xff0c;应用层。    其中以太网&#xff08;Ethernet&#xff09;的数据帧在链路层    IP包在网络层    TCP或UDP包在传输层    TCP或UDP中的数据&…

Redis的七种武器及其适合的应用场景

长生剑、孔雀翎、碧玉刀、多情环、离别钩、霸王枪、拳头是古龙笔下的七种武器&#xff0c;而本文打算将Redis的几种使用方式 Strings、Hashs、Lists、Sets、Sorted Sets、Pub/Sub、Transactions 也比作七种武器&#xff0c;为大家讲解Redis的七种特性&#xff0c;并列举其适合的…

涂鸦的这套宠物SDK设计,真香

我应该在之前的文章里面说过&#xff0c;我之前创业的时候做过宠物方面的产品&#xff0c;而且我们当时用的是乐鑫的芯片。最近知道在涂鸦工作的朋友也在研究这方面&#xff0c;他给我寄了几个小板子&#xff0c;还有涂鸦的IOT SDK&#xff0c;我玩了几天&#xff0c;觉得真的很…

三个周年纪念日

六月二十七日&#xff0c;二十八日&#xff0c;二十九日毕业一年&#xff0c;抵京一年&#xff0c;工作一年。此刻我身处远离北京的偏僻小县……不是被发配——自己的选择。 此一年&#xff0c;改变了许多&#xff0c;坚持了许多……此一年&#xff0c;收获了一些&#xff0c;付…

sql server常用函数积累

1.LEFT(character,integer) 参数1&#xff1a;要截取的字符串&#xff0c;参数2&#xff1a;截取字符个数 返回从字符串左边开始指定个数的字符 2.RIGHT(character,integer) 参数1&#xff1a;要截取的字符串&#xff0c;参数2&#xff1a;截取字符个数 返回从字符串右边开始指…