最近公司用了20年的产品,被客户抱怨莫名其妙的图像定在那里,过了好几分钟才恢复正常。经研究发现是因为X11的定时器存在缺陷,每次注册定时器下次执行的时间时,使用的是系统时间,如果系统时间被修改为较早的时间,则定时器任务不会被触发,得系统时间走到注册时间才会触发。
于是我进行了研究,刚开始考虑用线程加sleep来模拟定时器,后面查阅了资料发现,Linux内核也有定时器接口timer_create,并且它强大之处是可以选择定时器创建的时钟模式,我选用了CLOCK_MONOTONIC模式,即创建一个以系统启动时间为时钟的定时器,它不受系统时间改变的影响。
timer_create在创建时还可以选择用线程还是信号的方式,一开始我用了线程方式,发现它是每次定时器触发都会新建一个线程,当定时器触发频繁的时候这样并不好,所以后面我改用了信号触发的方式。
因为timer_create在一个进程中只能创建一个定时器,所以我们只能用它创建一个原子定时器,然后通过加定时器任务的方式实现多个定时器。
那么定时器加好后,能不能直接在定时器里面处理画图等工作呢,答案是不能。因为信号中断如果发生在malloc加锁处,在定时器处理中也遇到malloc,将会死锁。所以我想是不是可以使用事件触发,XSendEvent将定时器任务当做事件发到主事件循环中去,发现XtAppNextEvent它又是阻塞的,当界面有操作,这个接口才会触发返回,XSendEvent发过去的事件不会唤醒它。后面通过查阅资料发现XtAppAddWorkProc,它可以添加工作函数在后台执行,即XtAppNextEvent阻塞时,没事做的时候,就执行XtAppAddWorkProc添加的工作函数。
当然,实际过程比上面讲的更复杂曲折,这里写文简略了,分享出来,就是让后来人少走弯路,确实这块儿东西,网上的资料太少了。同时留个记录,也方便自己日后查阅。
下面贴上主要代码:
CApp.h
#include <signal.h>
#include <time.h>
#include <vector>
using namespace std;
struct M_SubTimerTask
{
int id;
int value;//定时时间(ms)
int interval; //间隔时间(ms)
int intervaltemp;
void(*handler)(); //处理函数
};
extern vector <M_SubTimerTask> g_SubTimerTasks;
extern timer_t g_TimerId;
typedef void (*TimerThreadHandlerType) (sigval_t);
int M_AddTimer(TimerThreadHandlerType handle, timer_t *retTimeId, int id);
int M_StartTimer(timer_t timer, __time_t value_sec, long int value_nsec, __time_t interval_sec, long int interval_nsec);
void M_DeleteTimer(timer_t timerid);
int M_AddSubTimerTask(void (*handle)(), int value, int interval);
void M_DeleteSubTimerTask(int id);
class CApp : public CAppContext
{
...
public:
int tm1,tm2,tm3,tm4,tm5,tm6,tm7,tm8,tm9,tm10;
static void OnTimer1();
static void OnTimeTimer1();
static void OnNetTimer1();
static void OnCheckTimer1();
static void OnOnceTimer1();
static void OnAutoBypassTimer1();
static void OnInitSectorTimer1();
static void OnAlertViewsTimer1();
static void OnTimerClearLog1();
static void OnReadFromFileTimer1();
static void OnCheckPlanStateTimer1();
static void OnCheckRemoveLogTimer1();
void OnTimer2();
void OnTimeTimer2();
void OnNetTimer2();
void OnCheckTimer2();
void OnOnceTimer2();
void OnAutoBypassTimer2();
void OnInitSectorTimer2();
void OnAlertViewsTimer2();
void OnTimerClearLog2();
void OnReadFromFileTimer2();
void OnCheckPlanStateTimer2();
void OnCheckRemoveLogTimer2();
void AddAllTimers();
void DeleteAllTimers();
}
CApp.cpp
//原子定时器时间片 1ms
#define TimerTimeUnit 1
//原子定时器id
timer_t g_TimerId = 0;
//所有定时器任务
vector <M_SubTimerTask> g_SubTimerTasks;
//定时器事件触发标记
bool g_bTimerEvent[10] = {false};
//定时器事件处理工作函数id
XtWorkProcId g_nTimerEventWorkProcId = 0;
pthread_mutex_t mutex_timer;
void TimerHandler (sigval_t v)
{
/*
if (pthread_mutex_lock(&mutex_timer) != 0){
printf("lock error!\n");
}
for(int i = 0; i < g_SubTimerTasks.size(); i++){
if(g_SubTimerTasks[i].value > 0){
g_SubTimerTasks[i].value -= TimerTimeUnit;
if(g_SubTimerTasks[i].value <= 0){
g_SubTimerTasks[i].handler();
g_SubTimerTasks[i].value = 0;
}
}else{
if(g_SubTimerTasks[i].intervaltemp > 0){
g_SubTimerTasks[i].intervaltemp -= TimerTimeUnit;
if(g_SubTimerTasks[i].intervaltemp <= 0){
g_SubTimerTasks[i].handler();
g_SubTimerTasks[i].intervaltemp = g_SubTimerTasks[i].interval;
}
}else{
//intervaltemp<=0,表示不重复执行函数
}
}
}
// 解锁
pthread_mutex_unlock(&mutex_timer);
*/
}
//原子定时器信号处理回调函数
void SigTimerHandler(int signo)
{
for(int i = 0; i < g_SubTimerTasks.size(); i++){
if(g_SubTimerTasks[i].value > 0){
g_SubTimerTasks[i].value -= TimerTimeUnit;
if(g_SubTimerTasks[i].value <= 0){
g_SubTimerTasks[i].handler();
g_SubTimerTasks[i].value = 0;
}
}else{
if(g_SubTimerTasks[i].intervaltemp > 0){
g_SubTimerTasks[i].intervaltemp -= TimerTimeUnit;
if(g_SubTimerTasks[i].intervaltemp <= 0){
g_SubTimerTasks[i].handler();
g_SubTimerTasks[i].intervaltemp = g_SubTimerTasks[i].interval;
}
}else{
//intervaltemp<=0,表示不重复执行函数
}
}
}
}
/*
int M_AddTimer(TimerThreadHandlerType handle, timer_t *retTimeId, int id){
struct sigevent evp;
memset (&evp, 0, sizeof (evp));
int ret = 0;
evp.sigev_notify = SIGEV_THREAD;
evp.sigev_notify_function = handle;
evp.sigev_value.sival_int = id;
ret = timer_create(CLOCK_MONOTONIC, &evp, retTimeId);
if(ret)
perror("My_AddTimer error:");
return ret;
}
*/
//添加原子定时器函数
int M_AddTimer(TimerThreadHandlerType handle, timer_t *retTimeId, int id){
struct sigevent evp;
memset (&evp, 0, sizeof (evp));
int ret = 0;
evp.sigev_notify = SIGEV_SIGNAL;
evp.sigev_signo = SIGUSR2;
if(signal(SIGUSR2, SigTimerHandler) == SIG_ERR)
printf("can not catch SIGUSR2\n");
ret = timer_create(CLOCK_MONOTONIC, &evp, retTimeId);
if(ret)
perror("My_AddTimer error:");
return ret;
}
//启动原子定时器函数
int M_StartTimer(timer_t timer, __time_t value_sec, long int value_nsec, __time_t interval_sec, long int interval_nsec){
struct itimerspec ts;
int ret = 0;
ts.it_value.tv_sec = value_sec;
ts.it_value.tv_nsec = value_nsec;
ts.it_interval.tv_sec = interval_sec;
ts.it_interval.tv_nsec = interval_nsec;
ret = timer_settime(timer, 0, &ts, NULL);
if( ret )
perror("My_StartTimer error:");
return ret;
}
//删除原子定时器函数
void M_DeleteTimer(timer_t timerid){
timer_delete(timerid);
}
//添加定时器任务函数
int M_AddSubTimerTask(void (*handle)(), int value, int interval)
{
static int id = 0;
id++;
if(id >= 0x7FFFFFFF){
id = 1;
}
M_SubTimerTask task;
task.handler = handle;
task.value = value;
task.interval = interval;
task.intervaltemp = task.interval;
task.id = id;
g_SubTimerTasks.push_back(task);
return id;
}
//删除定时器任务函数
void M_DeleteSubTimerTask(int id){
for(int i = 0; i < g_SubTimerTasks.size(); i++){
if(g_SubTimerTasks[i].id == id){
g_SubTimerTasks.erase(g_SubTimerTasks.begin() + i);
break;
}
}
}
//定时器事件处理工作函数
Boolean M_TimerEventProc(XtPointer p){
// printf("M_TimerEventProc\n");
if(g_bTimerEvent[0] == true){
GetApp()->OnTimerClearLog2();
g_bTimerEvent[0] = false;
}
if(g_bTimerEvent[1] == true){
GetApp()->OnTimer2();
g_bTimerEvent[1] = false;
}
if(g_bTimerEvent[2] == true){
GetApp()->OnInitSectorTimer2();
g_bTimerEvent[2] = false;
}
if(g_bTimerEvent[3] == true){
GetApp()->OnReadFromFileTimer2();
g_bTimerEvent[3] = false;
}
if(g_bTimerEvent[4] == true){
GetApp()->OnNetTimer2();
g_bTimerEvent[4] = false;
}
if(g_bTimerEvent[5] == true){
GetApp()->OnTimeTimer2();
g_bTimerEvent[5] = false;
}
if(g_bTimerEvent[6] == true){
GetApp()->OnCheckTimer2();
g_bTimerEvent[6] = false;
}
if(g_bTimerEvent[7] == true){
GetApp()->OnOnceTimer2();
g_bTimerEvent[7] = false;
}
if(g_bTimerEvent[8] == true){
GetApp()->OnAlertViewsTimer2();
g_bTimerEvent[8] = false;
}
if(g_bTimerEvent[9] == true){
GetApp()->OnAutoBypassTimer2();
g_bTimerEvent[9] = false;
}
return FALSE;
}
void CApp ::Init()
{
//加入定时器
//2021 lcl createTimer
// 初始化互斥锁
//if (pthread_mutex_init(&mutex_timer, NULL) != 0){
// 互斥锁初始化失败
// return;
//}
if(M_AddTimer(&TimerHandler, &g_TimerId, 1))
return;
if(M_StartTimer(&g_TimerId,0,1000*1000*TimerTimeUnit,0,1000*1000*TimerTimeUnit))
return;
AddAllTimers();
g_nTimerEventWorkProcId = XtAppAddWorkProc(GetAppcontext(), &M_TimerEventProc, NULL);
}
void CApp::OnTimerClearLog1()
{
g_bTimerEvent[0] = true;
}
void CApp::OnTimer1()
{
g_bTimerEvent[1] = true;
}
void CApp::OnInitSectorTimer1()
{
g_bTimerEvent[2] = true;
}
void CApp::OnReadFromFileTimer1()
{
g_bTimerEvent[3] = true;
}
void CApp::OnNetTimer1()
{
g_bTimerEvent[4] = true;
}
void CApp::OnTimeTimer1()
{
g_bTimerEvent[5] = true;
}
void CApp::OnCheckTimer1()
{
g_bTimerEvent[6] = true;
}
void CApp::OnOnceTimer1()
{
g_bTimerEvent[7] = true;
}
void CApp::OnAlertViewsTimer1()
{
g_bTimerEvent[8] = true;
}
void CApp::OnAutoBypassTimer1()
{
g_bTimerEvent[9] = true;
}
void CApp::OnTimerClearLog2(){
...
}
void CApp::OnTimer2()
{
...
}
void CApp::OnTimeTimer2()
{
...
}
void CApp::OnNetTimer2(){
...
}
void CApp::OnCheckTimer2()
{
...
}
void CApp::OnOnceTimer2()
{
...
}
void CApp::OnAutoBypassTimer2()
{
...
}
void CApp::OnInitSectorTimer2()
{
...
}
void CApp::OnAlertViewsTimer2()
{
...
}
void CApp::OnReadFromFileTimer2()
{
...
}
void CApp::OnCheckPlanStateTimer2()
{
...
}
void CApp::OnCheckRemoveLogTimer2()
{
...
}
void CApp::AddAllTimers(){
tm1 = M_AddSubTimerTask(&CApp::OnTimerClearLog1, 1000*3600, 1000*3600);
tm2 = M_AddSubTimerTask(&CApp::OnTimer1, 1000, 1000);
tm3 = M_AddSubTimerTask(&CApp::OnInitSectorTimer1, 300, 0);
#ifdef READ_FROM_FILE
tm4 = M_AddSubTimerTask(&CApp::OnReadFromFileTimer1, 400, 10);
#endif
tm5 = M_AddSubTimerTask(&CApp::OnNetTimer1, 400, 50);
tm6 = M_AddSubTimerTask(&CApp::OnTimeTimer1, 1000, 500);
tm7 = M_AddSubTimerTask(&CApp::OnCheckTimer1, 5000, 8000);
tm8 = M_AddSubTimerTask(&CApp::OnOnceTimer1, 6000, 0);
tm9 = M_AddSubTimerTask(&CApp::OnAlertViewsTimer1, 1000, 1000);
if(m_bByPassValid){
tm10 = M_AddSubTimerTask(&CApp::OnAutoBypassTimer1, 10*1000, 1000);
}
}
void CApp::DeleteAllTimers(){
M_DeleteSubTimerTask(tm1);
M_DeleteSubTimerTask(tm2);
M_DeleteSubTimerTask(tm3);
M_DeleteSubTimerTask(tm4);
M_DeleteSubTimerTask(tm5);
M_DeleteSubTimerTask(tm6);
M_DeleteSubTimerTask(tm7);
M_DeleteSubTimerTask(tm8);
M_DeleteSubTimerTask(tm9);
M_DeleteSubTimerTask(tm10);
}