Linux使用信号量sem_timedwait当作定时器

主要是通过判断信号量等待超时,然后达到计时的目的。

头文件:

#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;
}

测试结果:

  • 8
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值