LInux编程 解决X11定时器因系统时间改变而失效的缺陷 timer_create使用

     最近公司用了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);
}

猜你喜欢

转载自blog.csdn.net/lazydz/article/details/114394445
今日推荐