linux网络编程二十三:高性能定时器之时间堆

前面我们讨论的定时方案都是以固定的频率调用心跳函数tick,并在其中依次检测到期的定时器,然后执行到期定时器上的回调函数。

设计定时器的另外一种思路是:将所有定时器中超时时间最小的一个作为心跳间隔。这样,一旦心跳函数tick被调用,超时时间最小的定时器必然到期,我们就可以在tick函数中处理该定时器。然后再次从剩余的定时器中找出超时时间最小的一个,并将这段最小时间设置为下一次心跳间隔。如此反复,就实现了较为精确的定时。

最小堆很适合处理这种定时方案,最小堆是指每个节点的值都小于或等于其子节点的值的完全二叉树。

下图所示了一个具有6个元素的最小堆:


树的基本操作是插入节点和删除节点。对最小堆而言,它们都很简单。为了将一个元素X插入最小堆,我们可以在树的下一个空闲位置创建一个空穴。如果X可以放在空穴中而不被破坏堆的序,则插入完成。否则就执行上虑操作,即交换空穴和它的父节点上的元素。不断执行上述过程,直到X可以被放入空穴,则插入操作完成。

最小堆的删除操作是指删除其根节点上的元素,并且不破坏堆序性质。执行删除操作时,我们需要先在根节点处创建一个空穴;由于堆现在少了一个元素,因此我们可以把堆的最后一个元素X移动到该堆的某个地方。如果X可以被放入空穴,则删除操作完成;否则就执行下虑操作,即交换空穴和它的两个子节点中的较小者。不断进行上述操作,直到X可以被放入空穴,则删除操作完成。

由于最小堆是一种完全二叉树,所以我们可以用数组来组织其中的元素。

下图所示的是最小堆的数组表示:


对于数组中的任意一个位置i上的元素,其左子节点在位置2i+1上,其右子节点在2i+2上,其父节点则在[(i-1)/2](i>0)上。与用链表来表示堆相比,用数组表示堆不仅节省空间,而且更容易实现堆的插入、删除操作。

假设我们已经有一个包含N个元素的数组,现在要把它初始化为一个最小堆。那么最简单的方法是:初始化一个空堆,然后将数组中的每个元素插入该堆中。不过这样做的效率偏低。实际上,我们只需要对数组中的第[(N-1)/2]到0个元素执行一虑操作,即可确保该数组构成一个最小堆。

这是因为对包含N个元素的完全二叉树而言,它具有[(N-1)/2]个非叶子节点,这些非叶子节点正是该完全二叉树的第0个到第[(N-1)/2]个节点。
我们只要确保这些非叶子节点构成的子节点都具有堆序性质,整个树就具有堆序性质。

对时间堆而言,添加一个定时器的时间复杂度是O(lgn),删除一个定时器的时间复杂度是O(1),执行一个定时器的时间复杂度是O(1),因此,时间堆的效率是很高的。

1. 下面我们用最小堆实现一个定时器,其中最小堆用数组来表示。

//time_heap.h
#ifndef __TIME_HEAD__
#define __TIME_HEAD__

#include <stdio.h>
#include <netinet/in.h>
#include <time.h>


#define BUFFER_SIZE 64

class heap_timer;

//客户端数据
struct client_data
{
    sockaddr_in address;
    int sockfd;
    char buf[BUFFER_SIZE];
    heap_timer *timer;
};

//定时器
class heap_timer
{
public:
    heap_timer(int delay)
    {
        expire = time(NULL) + delay;
    }

public:
    time_t expire;                  //定时器生效的绝对时间
    void (*cb_func)(client_data*);  //定时器的回调函数
    client_data *user_data;         //客户端数据
};

//时间堆
class time_heap
{
public:
    //构造之一:初始化一个大小为cap的空堆
    time_heap(int cap);

    //构造之二:用已用数组来初始化堆
    time_heap(heap_timer **init_array, int size, int capacity);
    
    //销毁时间堆
    ~time_heap();

    //添加定时器timer
    int add_timer(heap_timer *timer);
    
    //删除定时器timer
    void del_timer(heap_timer *timer);

    //获得堆顶部的定时器
    heap_timer* top() const;

    //删除堆顶部的定时器
    void pop_timer();

    //心跳函数
    void tick();

    //堆是否为空
    bool empty()const { return cur_size == 0; }

pr
  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
Linux多线程服务端编程是指在Linux操作系统上开发多线程的服务器程序。而muduo C网络库(陈硕)是一个专门用于开发高性能网络服务器的C++库。 muduo C网络库是在muduo网络库的基础上进行了移植和改造,使其支持使用C语言进行开发。它提供了一系列高效的网络编程组件和工具,以简化多线程网络服务器的开发过程。 muduo C网络库基于Reactor模式,并且在设计上遵循了高并发、低延迟、高性能的原则。它可以处理上千个并发连接,并且具有稳定性和可靠性。 通过muduo C网络库,开发人员能够方便地实现多线程网络服务器。它提供了事件处理循环、网络IO、定时器、线程池等基本组件,可以有效地管理和处理网络连接。此外,muduo C网络库还提供了高效的多线程同步和协调机制,以支持服务器的并发处理能力。 使用muduo C网络库,开发人员可以简化网络服务器的编程过程,同时提高服务器的性能和扩展性。相比于手动处理底层网络细节,使用muduo C可以更加专注于业务逻辑的实现,提升开发效率。 总结来说,Linux多线程服务端编程使用muduo C网络库可以轻松开发高性能的网络服务器。它提供了丰富的组件和工具,满足了开发人员高并发、低延迟、高性能的需求。无论是大型互联网应用还是分布式系统,muduo C网络库都是一个可靠的选择。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值