基于C++标准库实现定时器类

基于C++标准库实现定时器类

定时器类是多线程编程中经常设计到的工具类
简单的定时器原理其实很简单(是不是有点GNU is not unix的味道;):

  • 创建一个新线程
  • 在那个线程里等待
  • 等待指定时长后做任务

python标准库中就有这么一个定时器类:threading.Timer
此类表示一个操作应该在等待一定的时间之后运行 --- 相当于一个定时器。
Timer 类是 Thread 类的子类,因此可以像一个自定义线程一样工作。
与线程一样,通过调用 start() 方法启动定时器。 而 cancel() 方法可以停止计时器(在计时结束前)。

例如:

def hello():
    print("hello, world")

t = Timer(30.0, hello)
t.start()  # after 30 seconds, "hello, world" will be printed

class threading.Timer(interval, function, args=None, kwargs=None) 创建一个定时器,在经过 interval 秒的间隔事件后,将会用参数 args 和关键字参数 kwargs 调用 function。
如果 args 为 None (默认值),则会使用一个空列表。如果 kwargs 为 None (默认值),则会使用一个空字典。

start() 启动定时器。

cancel() 停止定时器并取消执行计时器将要执行的操作。仅当计时器仍处于等待状态时有效。

接下来我将给出两种C++的Timer实现,接口类似于python的threading.Timer,不过精度为秒级的,其原因有二:

  • 实现代码参考了Posix多线程编程里的alarm实例程序,为了方便大家对比C语言版本的实现,这里也以秒为单位
  • 避免一上来过多的涉及C++标准库中<chrono>的用法,使代码逻辑更清晰的集中在定时器相关的部分

当然,作为一个在产品级代码中可用的Timer,精度至少应该在毫秒级才行,所以文章最后也会给出精度在微秒的代码实现的链接。

首先,给出C++版本的Timer的接口定义:
几乎完全仿照python的threading.Timer,

class Timer {
public:
    typedef std::function<void ()> Callback;

    Timer(int interval, Callback function); 
    void start();
    void cancel(); 
};
  • Callback:类型为std::function<void ()>,即返回类型为void的“函数”,当然在C++里可以是普通函数,函数对象,lambda等。
  • Timer(interval, function):构造函数,创建一个Timer,interval秒后到期(相对于调用start函数时的时间点),回调函数为function。
  • start():启动定时器。
  • cancel():停止定时器并取消执行计时器将要执行的操作。

在给出C++的实现前,我们先给出测试驱动程序。测试驱动程序来源于《Posix多线程程序设计》(英文原版书名为Programming with POSIX Threads)里的闹钟实例程序。
而我接下来的介绍顺序源于我的编码实现顺序,如下:

  • 先给出书中基于pthread的多线程版本的实例代码,C代码
  • 将C版本的代码转化成等价的python代码,基于threading.Timer接口实现的版本
  • 将python版本的代码,转化成C++版本,并基于C++的Timer类接口,得到C++的Timer类的测试驱动代码
  • 实现C++版的Timer类,并且编译测试驱动代码,运行验证

那么,我先给出基于pthread的多线程版本的实例代码,C代码:

/*
 * alarm_fork.c
 *
 * This version of alarm.c uses pthread_create to create a
 * separate thread to wait for each alarm to expire.
 */
#include <pthread.h>
#include "errors.h"

typedef struct alarm_tag {
    int         seconds;
    char        message[64];
} alarm_t;

void *alarm_thread (void *arg)
{
    alarm_t *alarm = (alarm_t*)arg;
    int status;

    status = pthread_detach (pthread_self ());
    if (status != 0)
        err_abort (status, "Detach thread");
    sleep (alarm->seconds);
    printf ("(%d) %s\n", alarm->seconds, alarm->message);
    free (alarm);
    return NULL;
}

int main (int argc, char *argv[])
{
    int status;
    char line[128];
    alarm_t *alarm;
    pthread_t thread;

    while (1) {
        printf ("Alarm> ");
        if (fgets (line, sizeof (line), stdin) == NULL) exit (0);
        if (strlen (line) <= 1) continue;
        alarm = (alarm_t*)malloc (sizeof (alarm_t));
        if (alarm == NULL)
            errno_abort ("Allocate alarm");

        /*
         * Parse input line into seconds (%d) and a message
         * (%64[^\n]), consisting of up to 64 characters
         * separated from the seconds by whitespace.
         */
        if (sscanf (line, "%d %64[^\n]", 
            &alarm->seconds, alarm->message) < 2) {
            fprintf (stderr, "Bad command\n");
            free (alarm);
        } else {
            status = pthread_create (
                &thread, NULL, alarm_thread, alarm);
            if (status != 0)
                err_abort (status, "Create alarm thread");
        }
    }
}

代码的完整说明参见《Posix多线程程序设计》 1.5.3章节,这里就不再搬运原文了。

接下来是移植成python版本的代码:

#!/usr/bin/env python3

from threading import Timer

class Alarm:
    def __init__(self, seconds:int, message:str):
        self.seconds = seconds
        self.message = message

def callback(alarm:Alarm):
    print("({}) {}\n".format(alarm.seconds, alarm.message))

if __name__ == "__main__":
    while True:
        line = input("Alarm> ")
        if len(line) <= 1:
            continue

        try:
            seconds, *message = line.split(' ')
            alarm = Alarm(int(seconds), ' '.join(message))
            t = Timer(interval=int(seconds), function=callback, args=(alarm, ))
            t.start()
        except:
            print("Bad command")

python版本的代码大家有兴趣的可以在本地运行一下,看看效果;)

再然后,我把这段代码翻译成C++版本的,基于C++的Timer类接口:

cpp

#include "timer.hpp"
#include <cstdlib>
#include <string>
#include <memory>
#include <iostream>

struct Alarm {
    Alarm(int seconds_, const std::string& message_): 
        seconds(seconds_), message(message_) {
    }

    int seconds;
    std::string message;
};

void callback(std::shared_ptr<Alarm> alarm) {
    std::cout << "(" << alarm->seconds << ") " << alarm->message << std::endl;
}

std::tuple<int, std::string> parse_command(const std::string& line) {
    auto pos = line.find(' ');
    if (pos == std::string::npos)
        throw std::runtime_error("invalid line: separator not found");

    int seconds = std::stoi(line.substr(0, pos));
    std::string message = line.substr(pos+1);
    return std::make_tuple(seconds, message);
}

int main()
{
    std::string line;
    int seconds;
    std::string message;
    while (true) {
        std::cout << "Alarm> ";
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值