主要是通过判断信号量等待超时,然后达到计时的目的。
头文件:
#include<semaphore.h>
信号量初始化
sem_init(&p_sem, 0, 0);
获取当前系统时间
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
此函数用来获得当前时间,结果存入 struct timespec。CLOCK_REALTIME指从1970.1.1到目前的时间。
/* Get current value of clock CLOCK_ID and store it in TP. */
extern int clock_gettime (clockid_t __clock_id, struct timespec *__tp) __THROW;
/* Identifier for system-wide realtime clock. */
# define CLOCK_REALTIME 0
struct timespec结构体存储的是时间戳,某一刻的时间,一个成员表示秒,一个成员表示纳秒。
/* POSIX.1b structure for a time value. This is like a `struct timeval' but
has nanoseconds instead of microseconds. */
struct timespec
{
__time_t tv_sec; /* Seconds. */
__syscall_slong_t tv_nsec; /* Nanoseconds. */
};
// 等待信号量,超时时间为下一次调用的时间点
sem_timedwait(p_sem, &ts);
如果p_sem 信号量值>0,则sem_timedwait 立即返回;如果sem 信号量值≤0,则 sem_timedwait 阻塞等待 TIMEOUT秒后再返回。
第二个参数表示TIMEOUT时间,注意这个地方,它指定一个时间点而不是时间段,这个时间点用结构体表示,结构体以自1970-01-01 00:00:00起,到目前为止的时间。
这个时间点要晚于调用时的时间点,可以理解成阻塞到未来某个时间点。
这个超时的时间就相当于定时了。跟sleep一段时间类似。而且与C++的std::this_thread::sleep_for 里面的sleep相比,效果差不多。
写个例子验证一下:
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
#include <time.h>
#include <errno.h>
#include <iostream>
#include <chrono>
#include <thread>
using namespace std;
int main(int argc, const char *argv[])
{
sem_t p_sem;
int ret = 0;
int value = 0;
/* 创建信号量 */
//p_sem = sem_open("mysem2", O_CREAT, 0666, 1); /* 信号量初值为1 */
sem_init(&p_sem, 0, 0);
const int count=200;
u_int64_t count1 = 0, count2 = 0;
for (int i=0; i<count; ++i)
{
struct timespec ts, ts_end;
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_nsec += 100000000; //100ms
if (ts.tv_nsec >= 1000000000)
{
ts.tv_nsec -= 1000000000;
ts.tv_sec += 1;
}
// 等待信号量,超时时间为下一次调用的时间点
sem_timedwait(&p_sem, &ts); //超时时间100ms
clock_gettime(CLOCK_REALTIME, &ts_end);
count1 += (ts_end.tv_nsec - (ts.tv_nsec - 100000000) + (ts_end.tv_sec - ts.tv_sec) * 1000000000);
}
for (int j=0; j<count; j++)
{
auto begin = std::chrono::system_clock::now();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
auto end = std::chrono::system_clock::now();
auto result = std::chrono::duration_cast<std::chrono::nanoseconds>(end - begin).count();
count2 += result;
}
//对比sem_timedwait和sleep_for的精确度,运行相同次数和相同的定时后取平均值,值越接近100000000越精确
std::cout<<"sem_timedwait 平均时间: "<<(count1/count)<<" \nsleep_for平均时间: \t"<<(count2/count)<<std::endl;
sem_destroy(&p_sem);
getchar();
return 0;
}
#g++ -o sem sem.cpp -lpthread
程序先用clock_gettime(CLOCK_REALTIME, &ts); 获取当前时间,然后通过
ts.tv_nsec += 100000000;
延时100ms。这里的 tv_nsec有可能溢出,所以要判断处理一下。
if (ts.tv_nsec >= 1000000000)
{
ts.tv_nsec -= 1000000000;
ts.tv_sec += 1;
}
然后调用sem_timedwait(p_sem, &ts);等待超时。超时后调用
clock_gettime(CLOCK_REALTIME, &ts_end);
获取调用后的时间,与开始的时间做个差值得到程序运行时间。为了更准确,循环200此取平均值。
为了对比效果,下面使用std::this_thread::sleep_for()进行了对比,同样休眠100ms,运行200次取平均值,并实验多次。
第一次结果:
第二次结果:
第三次结果:
第四次结果:
第五次结果:
第六次结果:
可以看出sem_timedwait()和 std::this_thread::sleep_for的效果差不多。
如果二者都支持的环境下,使用std::this_thread::sleep_for更方便一些。
如果是C语言环境,可以使用sem_timedwait,封装一下,具有定时功能,同样能保证精度。
封装定时函数
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
#include <time.h>
#include <errno.h>
#include <iostream>
#include <chrono>
#include <thread>
using namespace std;
//纳秒
void Sleep_Sem_ns(uint64_t nanoseconds)
{
const uint64_t ONE_SECOND_MS = 1000000000;
sem_t p_sem;
sem_init(&p_sem, 0, 0);
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += nanoseconds / ONE_SECOND_MS;
ts.tv_nsec += nanoseconds % ONE_SECOND_MS;
if (ts.tv_nsec >= ONE_SECOND_MS)
{
ts.tv_nsec -= ONE_SECOND_MS;
ts.tv_sec += 1;
}
sem_timedwait(&p_sem, &ts); //超时时间100ms
sem_destroy(&p_sem);
}
//毫秒
void Sleep_Sem_ms(uint64_t milliseconds)
{
Sleep_Sem_ns(milliseconds * 1000 * 1000);
}
//秒
void Sleep_Sem_s(uint64_t seconds)
{
Sleep_Sem_ns(seconds * 1000 * 1000 * 1000);
}
测试一下是否好用
int main(int argc, const char *argv[])
{
auto begin = std::chrono::system_clock::now();
Sleep_Sem_ns(100000000);
auto end = std::chrono::system_clock::now();
std::cout<< std::chrono::duration_cast<std::chrono::nanoseconds>(end - begin).count() <<std::endl;
begin = std::chrono::system_clock::now();
Sleep_Sem_ms(200);
end = std::chrono::system_clock::now();
std::cout<< std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count() <<std::endl;
begin = std::chrono::system_clock::now();
Sleep_Sem_s(2);
end = std::chrono::system_clock::now();
std::cout<< std::chrono::duration_cast<std::chrono::seconds>(end - begin).count() <<std::endl;
return 0;
}
测试结果: