【Linux C/C++ 延时(延迟)函数比较】介绍Linux系统中常用的延时函数sleep、usleep、nanosleep、select和std::sleep_for()的区别和使用场景

简介: 【Linux C/C++ 延时(延迟)函数比较】介绍Linux系统中常用的延时函数sleep、usleep、nanosleep、select和std::sleep_for()的区别和使用场景

首先,需要了解各个睡眠函数的作用和使用场景。

 

  • sleep函数用于让进程休眠指定的秒数,适用于需要较长时间的休眠场景
  • usleep函数用于让进程休眠指定的微秒数,适用于需要较短时间的休眠场景,不精确
  • nanosleep函数用于让进程休眠指定的纳秒数,适用于需要纳秒级的休眠场景,不精确(因为这种级别会因为系统调度和其他因素而有所不同
  • select 的精度是微妙,用于在多个文件描述符之间选择可读或可写的文件描述符,并在指定的时间内等待它们之一变为可操作,它可以用作高精度的休眠函数,虽然这并不是它的主要用途。
  • std::sleep_for()可以支持任意时间段的休眠,包括微秒、毫秒、秒等,具有更加灵活的控制能力,在linux底层调用nanosleep,可以让线程休眠指定的时间量

其次,需要权衡各个睡眠函数的优缺点。

其次,需要权衡各个睡眠函数的优缺点。

例如,sleep函数的精度较低,适用于较长时间的休眠场景,但可能因为信号中断等原因提前结束;

usleep函数的精度较高,但也有被信号中断的风险;

nanosleep函数的精度更高,但使用稍微麻烦,使用nanosleep应注意判断返回值和错误代码,否则容易造成cpu占用率100%

select函数可以同时等待多个文件描述符,但有一定的限制,短延时推荐使用select函数,因为准确;

std::sleep_for()函数跨平台适用性好,但需要C++11支持。

最后,需要根据具体场景选择合适的睡眠函数。例如,如果需要在多个文件描述符之间选择可读或可写的文件描述符,并在指定的时间内等待它们之一变为可操作,可以使用select函数;如果需要让线程休眠指定的时间量,可以使用std::sleep_for()函数;如果需要更精确的休眠场景,可以使用nanosleep函数等。

总的来说,选择最佳的睡眠函数需要综合考虑使用场景、精度要求、可中断性等因素,并根据具体情况选择合适的睡眠函数,以提高系统的性能和稳定性。


usleep()有很大的问题

  • 在一些平台下不是线程安全,如HP-UX以及Linux
  • usleep()会影响信号
  • 在很多平台,如HP-UX以及某些Linux下,当参数的值必须小于1 * 1000 * 1000也就是1秒,否则该函数会报错,并且立即返回。.

大部分平台的帮助文档已经明确说了,该函数是已经被舍弃的函数。

 


无论是WinCE还是Linux操作系统,应用线程的运行总是涉及到两个基本的参数:一个是系统分配给线程的时间片,一个是系统调度的时间间隔。Linux和WinCE下这两个参数有所不同,如下表所示:

WinCE

嵌入式Linux

  线程的运行时间片

100ms

10ms

  系统调度间隔

1ms

10ms


#include <unistd.h>
 
unsigned int sleep(unsigned int seconds);

    //执行挂起指定的秒数

 

    返回值:

    完成seconds所指定的墙上时钟时间,返回0

    若由于进程捕捉到某个信号并从信号处理函数提早,返回时未休眠完的秒数

sleep()非系统调用,sleep()是在库函数中实现的,它是通过alarm()来设定报警时间,使用sigsuspend()将进程挂起在信号SIGALARM上。
sleep()只能精确到秒级上。sleep()会令目前的进程暂停,直到达到参数seconds 所指定的时间,或是被信号所中断.


int usleep(useconds_t usec);

    //把进程挂起一段时间, 单位是微秒(百万分之一秒);

 

    返回值:

    完成seconds所指定的墙上时钟时间,返回0

    若由于进程捕捉到某个信号并从信号处理函数提早,返回时未休眠完的微妙数


int nanosleep(const struct timespec *req, struct timespec *rem);

    //具有不影响任何信号的优点,它被POSIX标准化,它提供更高的定时分辨率,并且允许继续已被信号中断的睡眠更容易。

  参数:

struct timespec
              {
                      time_t  tv_sec;     //秒   
                      long    tv_nsec;     //纳妙 
              };

 req:秒或纳秒指定需要休眠的时间长度

 rem:如果某个信号中断了休眠间隔,进程没有终止,rem指向的结构体会被设置为未休眠完的时间长度,不感兴趣可设NULL。

    返回值:

    成功休眠所请求的间隔之后,返回0

    调用由信号处理程序中断或遇到错误,返回返回-1,其中errno设置为指示错误,并将剩余时间写入由rem指向的结构中。

 

这个函数功能是暂停某个进程直到你规定的时间后恢复,参数req就是你要暂停的时间,其中req->tv_sec是以秒为单位,而tv_nsec以毫微秒为单位(10的-9次方秒)。

由于调用nanosleep是是进程进入TASK_INTERRUPTIBLE,这种状态是会相应信号而进入TASK_RUNNING状态的,这就意味着有可能会没有等到你规定的时间就因为其它信号而唤醒,此时函数返回-1,切还剩余的时间会被记录在rem中。

这个函数让进程休眠指定的纳秒数,适用于需要更精确的休眠场景。然而,实际的休眠时间可能会因为系统调度和其他因素而有所不同,所以它并不一定能保证纳秒级别的精度。


int clock_nanosleep(clockid_t clock_id, int flags, const struct timespec *reqtp, struct timespec *remtp);

    //把进程挂起一段时间, 单位是微秒(百万分之一秒);

 

参数:

clock_id:指定了计算机延迟时间基于的时钟,下表所示

flags:用于控制延时是绝对的还是相对的

        当flags被设置为0的时候,睡眠时间是相对的(想要睡眠多久)

        当其数值被设置为TIMER_ABSTIME的时候,睡眠时间是绝对的,例如希望休眠到时钟到达某个特定时间。

req:秒或纳秒指定需要休眠的时间长度

rem:如果某个信号中断了休眠间隔,进程没有终止,rem指向的结构体会被设置为未休眠完的时间长度,不感兴趣可设NULL。

    返回值:

    成功休眠所请求的间隔之后,返回0

    调用由信号处理程序中断或遇到错误,返回返回-1,其中errno设置为指示错误,并将剩余时间写入由rem指向的结构中。


int select(int nfds, fd_set *readfds, fd_set *writefds, 
fd_set *exceptfds, struct timeval *timeout);

设置timeout的值,而将其他参数都置为NULL,当内部时间耗尽后select便会退出。

示例:

#include "time.h"
#include "sys/select.h"//必须调用这俩头文件
 
struct timeval tv;//声明一个timeval类型的对象tv
tv.tv_sec = 0;
tv.tv_usec = 2*1000;
select(0,NULL,NULL,NULL,&tv)

使用sleep()和usleep()的确可以达到效果,但是使用这类延时可能会导致系统产生未知问题,所以往往使用select函数,而且select的延时作用精度足够高.

几个注意事项:
1、tv_sec的初始化最好在tv_usec的前面
2、select的延时时间等于sec和usec时间之和
3、select的延时时间与设定值可能有1ms左右的误差
4、select每次运行之后,会将tv的值清零,所以如果要循环使用select,务必把tv.tv_usec的初始化放在循环中!


std::sleep_for()是C++11标准中提供的休眠函数,它可以使当前线程休眠指定的时间。函数原型如下:

#include <chrono>
#include <thread>
 
namespace std {
    template<class Rep, class Period>
    void sleep_for(const chrono::duration<Rep, Period>& rel_time);
 
    void sleep_for(const chrono::nanoseconds& ns);
    void sleep_for(const chrono::microseconds& us);
    void sleep_for(const chrono::milliseconds& ms);
    void sleep_for(const chrono::seconds& s);
    void sleep_for(const chrono::minutes& m);
    void sleep_for(const chrono::hours& h);
}

其中,第一个模板函数可以接受任意时间单位的参数,其他函数则分别接受纳秒、微秒、毫秒、秒、分、时的参数。

需要注意的是,std::sleep_for()函数会使当前线程休眠,因此应该谨慎使用,以免影响程序的正常运行。另外,由于休眠的精度受到系统调度器的影响,因此实际休眠时间可能略有偏差。

std::sleep_for()底层在 Linux 中使用的是nanosleep()函数实现的。nanosleep()函数可以使线程休眠指定的时间,精度可以达到纳秒级别。而std::sleep_for()是C++11标准中提供的休眠函数,它通过调用底层的nanosleep()函数实现线程休眠。因此,在 Linux 系统中,std::sleep_for()底层使用的就是nanosleep()函数。

下面是一个示例代码:

#include <iostream>
#include <chrono>
#include <thread>
 
using namespace std;
using namespace std::chrono;
 
int main() {
    auto start = high_resolution_clock::now();
 
    // 休眠1秒钟
    cout << "start sleeping..." << endl;
    std::this_thread::sleep_for(seconds(1));
    cout << "sleeping finished." << endl;
 
    auto end = high_resolution_clock::now();
    auto duration = duration_cast<milliseconds>(end - start);
    cout << "duration: " << duration.count() << "ms" << endl;
 
    return 0;
}


目录
打赏
0
1
1
1
216
分享
相关文章
|
1月前
|
linux中的目录操作函数
本文详细介绍了Linux系统编程中常用的目录操作函数,包括创建目录、删除目录、读取目录内容、遍历目录树以及获取和修改目录属性。这些函数是进行文件系统操作的基础,通过示例代码展示了其具体用法。希望本文能帮助您更好地理解和应用这些目录操作函数,提高系统编程的效率和能力。
153 26
|
3月前
|
【Linux】System V信号量详解以及semget()、semctl()和semop()函数讲解
System V信号量的概念及其在Linux中的使用,包括 `semget()`、`semctl()`和 `semop()`函数的具体使用方法。通过实际代码示例,演示了如何创建、初始化和使用信号量进行进程间同步。掌握这些知识,可以有效解决多进程编程中的同步问题,提高程序的可靠性和稳定性。
138 19
linux m、mm、mmm函数和make的区别
通过理解和合理使用这些命令,可以更高效地进行项目构建和管理,特别是在复杂的 Android 开发环境中。
104 18
嵌入式Linux系统编程 — 5.3 times、clock函数获取进程时间
在嵌入式Linux系统编程中,`times`和 `clock`函数是获取进程时间的两个重要工具。`times`函数提供了更详细的进程和子进程时间信息,而 `clock`函数则提供了更简单的处理器时间获取方法。根据具体需求选择合适的函数,可以更有效地进行性能分析和资源管理。通过本文的介绍,希望能帮助您更好地理解和使用这两个函数,提高嵌入式系统编程的效率和效果。
148 13
C++ 调用Linux系统命令
一个简单的C++程序,Test函数用来测试调用Linux的系统命令ls -l #include #include #include #include #include #include using namespace std; const i...
2225 0
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。
【C++进阶】特殊类设计 && 单例模式
通过对特殊类设计和单例模式的深入探讨,我们可以更好地设计和实现复杂的C++程序。特殊类设计提高了代码的安全性和可维护性,而单例模式则确保类的唯一实例性和全局访问性。理解并掌握这些高级设计技巧,对于提升C++编程水平至关重要。
39 16
类和对象(中 )C++
本文详细讲解了C++中的默认成员函数,包括构造函数、析构函数、拷贝构造函数、赋值运算符重载和取地址运算符重载等内容。重点分析了各函数的特点、使用场景及相互关系,如构造函数的主要任务是初始化对象,而非创建空间;析构函数用于清理资源;拷贝构造与赋值运算符的区别在于前者用于创建新对象,后者用于已存在的对象赋值。同时,文章还探讨了运算符重载的规则及其应用场景,并通过实例加深理解。最后强调,若类中存在资源管理,需显式定义拷贝构造和赋值运算符以避免浅拷贝问题。
类和对象(上)(C++)
本篇内容主要讲解了C++中类的相关知识,包括类的定义、实例化及this指针的作用。详细说明了类的定义格式、成员函数默认为inline、访问限定符(public、protected、private)的使用规则,以及class与struct的区别。同时分析了类实例化的概念,对象大小的计算规则和内存对齐原则。最后介绍了this指针的工作机制,解释了成员函数如何通过隐含的this指针区分不同对象的数据。这些知识点帮助我们更好地理解C++中类的封装性和对象的实现原理。
|
16天前
|
【c++】继承(继承的定义格式、赋值兼容转换、多继承、派生类默认成员函数规则、继承与友元、继承与静态成员)
本文深入探讨了C++中的继承机制,作为面向对象编程(OOP)的核心特性之一。继承通过允许派生类扩展基类的属性和方法,极大促进了代码复用,增强了代码的可维护性和可扩展性。文章详细介绍了继承的基本概念、定义格式、继承方式(public、protected、private)、赋值兼容转换、作用域问题、默认成员函数规则、继承与友元、静态成员、多继承及菱形继承问题,并对比了继承与组合的优缺点。最后总结指出,虽然继承提高了代码灵活性和复用率,但也带来了耦合度高的问题,建议在“has-a”和“is-a”关系同时存在时优先使用组合。
58 6