主要是通过判断信号量等待超时,然后达到计时的目的。
创建信号量
sem_t *p_sem = sem_open("mysem2", O_CREAT, 0666, 0);
获取当前系统时间
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
此函数用来获得当前时间,结果存入 struct timespec
/* 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 0struct 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);
如果sem 信号量值>0,则sem_timedwait 立即返回;如果sem 信号量值≤0,则 sem_timedwait 阻塞等待 TIMEOUT秒后再返回。
第二个参数表示TIMEOUT时间,注意这个地方,它指定一个时间点而不是时间段,这个时间点用结构体表示,结构体以自1970-01-01 00:00:00起,到目前为止的时间。
注意,这个时间点要晚于调用时的时间点,可以理解成阻塞到未来某个时间点。
这个超时的时间就相当于定时了。跟sleep一段时间类似。而且与C++的std::this_thread::sleep_for 里面的sleep相比,效果差不多。
写个例子验证一下:
//sem.cpp
#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;#define SEM_NAME "mysem" /* 定义信号量的名字 */int main(int argc, const char *argv[])
{sem_t *p_sem = NULL;int ret = 0;int value = 0;/* 创建信号量 */p_sem = sem_open("mysem2", O_CREAT, 0666, 1); /* 信号量初值为1 */const int count=200;u_int64_t count1 = 0, count2 = 0;for (int i=0; i<count; ++i){struct timespec ts, ts2; clock_gettime(CLOCK_REALTIME, &ts); ts.tv_nsec += 100000000; //延时100msif (ts.tv_nsec >= 1000000000){ts.tv_nsec -= 1000000000;ts.tv_sec += 1;}// 等待信号量,超时时间为下一次调用的时间点sem_timedwait(p_sem, &ts);clock_gettime(CLOCK_REALTIME, &ts2);count1 += (ts2.tv_nsec - ts.tv_nsec);}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-100000000);}std::cout<<(count1/count + 100000000)<<" "<<(count2/count + 100000000)<<std::endl;sem_close(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, &ts2);
获取调用后的时间,与开始的时间做个差值得到程序运行时间。为了更准确,循环200此取平均值。
为了对比效果,下面使用chrono进行了对比,同样休眠100ms,运行200次取平均值。
第一次结果:
100076958 100076941
第二次结果:
100079963 100080953
第三次结果:
100080090 100077299
第四次结果:
100079304 100082254
第五次结果:
100078986 100081476
第六次结果:
100077409 100070881
可以看出sem_timedwait()和 std::this_thread::sleep_for的效果差不多。
如果二者都支持的环境下,使用std::this_thread::sleep_for更方便一些。
如果是C语言环境,可以使用sem_timedwait,同样能保证精度。