Qt/C++ 中常用的几种高精度延时测试(其中三种Linux下可用)

这里博主统计了一下常见的几种延时方法,并做了一些延时精度测试,供大家参考:

  1. sleep()、msleep()、usleep()分别进行阻塞线程的时钟延时
  2. QWaitCondition::wait()+QMutex阻塞线程延时
  3. QEventLoop + QTimer非阻塞线程延时
  4. select()延时
  5. RTC硬件时钟计时

其中2、4、5种方法只在linux下可用。

新建子线程

/****************************************************************
 Doc    :   timethread.h
 Author :   BingLee
 Date   :   2021-05-27
 Info   :   Test high percision timer.
 https://blog.csdn.net/Bing_Lee (C)All rights reserved.
******************************************************************/
#ifndef TIMETHREAD_H
#define TIMETHREAD_H

#include <QThread>
#include <QMutex>
#include <QWaitCondition>

class TimeThread : public QThread
{
    Q_OBJECT
public:
    TimeThread(QObject *parent = NULL);
    void run();

    void sleepMethod1(int time_ms);    //sleep():/s  msleep():/ms  usleep():/us sleep函数是使调用sleep函数的线程休眠
    void sleepMethod2(int time_ms);    //wait():/ms wait是阻塞当前线程
    void sleepMethod3(int time_ms);    //QEventLoop + QTimer::singleShot:/ms 不阻塞线程,精度主要依靠QTimer精度
    void sleepMethod4(int time_ms);     //select():/ms linux 下通过select()函数控制延时,精度可达到us

private:
    QMutex m_pMutex;
    QWaitCondition m_waitCondition;

};

#endif // TIMETHREAD_H

方法一 msleep()

void TimeThread::sleepMethod1(int time_ms)
{
    //此处可以用根据实际需要的精度选择sleep():/s  msleep():/ms  usleep():/us
    msleep(time_ms);
    return;
}

方法二 QWaitCondition::wait()+QMutex

void TimeThread::sleepMethod2(int time_ms)
{
    m_pMutex.lock();
    m_waitCondition.wait(&m_pMutex, time_ms);
    m_pMutex.unlock();
    return;
}

方法三 QEventLoop + QTimer

void TimeThread::sleepMethod3(int time_ms)
{
    QEventLoop loop;                                //定义一个新的事件循环
    QTimer::singleShot( time_ms, &loop, SLOT( quit() ) ); //创建单次定时器,槽函数触发循环退出
    loop.exec();                                    //直到定时时间到,本循环被退出
}

方法四 select()

void TimeThread::sleepMethod4(int time_ms)
{
#if (defined Q_OS_LINUX)
    struct timeval timeOut;
    timeOut.tv_sec = 0; //延时单位/s
    timeOut.tv_usec = 1000 * time_ms; //延时单位/us
    select(1, NULL, NULL, NULL, &timeOut);
#else
    msleep(time_ms);
#endif
    return;
}

方法五 RTC硬件时钟

这里我借用两篇其他博主的文章,有兴趣可以自己测试下述代码

Linux下实现定时器Timer的几种方法

RTC机制利用系统硬件提供的Real Time Clock机制,通过读取RTC硬件/dev/rtc,通过ioctl()设置RTC频率,代码如下:

#include <stdio.h>
#include <linux/rtc.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
 
int main(int argc, char* argv[])
{
        unsigned long i = 0;
        unsigned long data = 0;
        int retval = 0;
        int fd = open ("/dev/rtc", O_RDONLY);
 
        if(fd < 0)
        {
                perror("open");
                exit(errno);
        }
 
        /*Set the freq as 4Hz*/
        if(ioctl(fd, RTC_IRQP_SET, 1) < 0)
        {
                perror("ioctl(RTC_IRQP_SET)");
                close(fd);
                exit(errno);
        }
        /* Enable periodic interrupts */
        if(ioctl(fd, RTC_PIE_ON, 0) < 0)
        {
                perror("ioctl(RTC_PIE_ON)");
                close(fd);
                exit(errno);
        }
 
        for(i = 0; i < 100; i++)
        {
                if(read(fd, &data, sizeof(unsigned long)) < 0)
                {
                        perror("read");
                        close(fd);
                        exit(errno);
 
                }
                printf("timer\n");
        }
        /* Disable periodic interrupts */
        ioctl(fd, RTC_PIE_OFF, 0);
        close(fd);
 
        return 0;
}

linux c的四种定时方式(sleep/usleep/select/ioctl)


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>   // gettimeofday sleep usleep
#include <sys/types.h> // open
#include <sys/stat.h>  // open
#include <fcntl.h>     // open
#include <errno.h>      // error
#include <unistd.h>     // close
#include <linux/rtc.h>  // rtc_commadn RTC_IRQP_SET / RTC_PIE_ON / RTC_PIE_OFF
#include <sys/ioctl.h>  // ioctl
/**g++ -o timer_test time_test.cc*/

/* 定义log输出宏 */
#define DEB_LOG(fmat,...)  printf("%s:%d_%s() "fmat"\n", \
    __FILE__, __LINE__, __FUNCTION__,##__VA_ARGS__)

/*设定时钟频率,频率可用范围是2~8192,一定要是2的n次方,8192时,一次约122微妙,*/
#define FREQ 8192
#define USEC_PER_SECOND 1000000
/*根据输入的等待待时间(毫秒),计算循环次数*/
#define CALC_CNT(millseconds) (millseconds * 1000.0 / USEC_PER_SECOND * FREQ + 0.5)


struct timeval tvs, tve;
void showTime(int startStop, long sleepT, const char* msg) {
    if (1 == startStop) {
        gettimeofday(&tvs, 0);   // 记录定时器开始时间
    } else {
        gettimeofday(&tve, 0);   // 记录定时器结束时间
        DEB_LOG("%s:    [%ldus] [%ldus]", msg, sleepT,
                (tve.tv_sec - tvs.tv_sec) * 1000000LL
                        + (tve.tv_usec - tvs.tv_usec));
    }
}

/* * 打开RTC时钟设备
 * freq: RTC 时钟的频率
 * fd:  读取RTC时钟的fd * */
int openRTC(int freq, int* fd) {
    /* 打开 RTC 时间设备 */
    *fd = open("/dev/rtc", O_RDONLY);
    if (*fd < 0) {
        DEB_LOG("open /dev/rtc NG, errno=%d msg=%s", errno, strerror(errno));
        return -1;
    }

    /* 设定 RTC 的时钟频率   2~8192Hz,最小精度为123微秒*/
    if (ioctl(*fd, RTC_IRQP_SET, freq) < 0) {
        DEB_LOG("ioctl(RTC_IRQP_SET)");
        close(*fd);
        return -1;
    }
    /* 启动 RTC 时钟  */
    if (ioctl(*fd, RTC_PIE_ON, 0) < 0) {
        DEB_LOG("ioctl(RTC_PIE_ON)");
        close(*fd);
        return -1;
    }
    return 0;
}

/* * 关闭RTC时钟设备
 * fd:  读取RTC时钟的fd * */
void closeRTC(int fd) {
    /* 关闭 RTC 时钟计时  */
    ioctl(fd, RTC_PIE_OFF, 0);
    /* 关闭 RTC 装置 */
    close(fd);
}

/*使用ioctl的情况下的,计时器函数*/
int rtcTimer(int millseconds, int fd) {
    int loopNum = CALC_CNT(millseconds);
    unsigned long data = 0;
    for (int i = 0; i < loopNum; i++) {
        /*read一次的时间 = 1秒/时钟频率 (频率范围 2~8192,最小精度为123微秒)*/
        if (read(fd, &data, sizeof(unsigned long)) < 0) {
            return -1;
        }
    }
    return 0;
}

int main(int argc, char* argv[]) {
    int sleepMs = 3;       // ms
    int sleepLoop = 5000;
    long sleepT = sleepMs * sleepLoop * 1000;

    /*##############  使用实时时钟 RTC 做定时 ################*/
    // 打开 RTC 时钟
    int fd;
    int ret = openRTC(FREQ, &fd);
    if (0 != ret) {
        DEB_LOG("openRTC error");
        return -1;
    }
    /* 使用 RTC 时钟计时   */
    showTime(1,sleepT,"ioctl RTC");
    for (int i = 0; i < sleepLoop; ++i) {
        rtcTimer(sleepMs, fd); /* 调用定时器函数 */
    }
    showTime(2,sleepT,"ioctl RTC");
    closeRTC(fd); //关闭 RTC 时钟
    return 0;
}

测试结果

对上边前四种方法在linux系统上做有一定负载的运行测试,结果如下:

这四种方法时钟的偏差基本都在1ms,稳定性来说QWaitCondition::wait()+QMutexselect()>msleep()>QEventLoop + QTimer

在这里插入图片描述

源码

源代码

GitHub 加速计划 / li / linux-dash
10.39 K
1.2 K
下载
A beautiful web dashboard for Linux
最近提交(Master分支:2 个月前 )
186a802e added ecosystem file for PM2 4 年前
5def40a3 Add host customization support for the NodeJS version 4 年前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐