Linux提供定時器機制,可以指定在未來的某個時刻發生某個事件,定時器的結構如下:
struct timer_list
{
struct list_head list;
unsigned long expires;
unsigned long data;
void (*function)(unsigned long);
};
list 實現的時候使用的,和定時器功能無關
expires 是定時器定時的滴答數(當前的滴答數為 jiffies )
function 到那個時刻內核調用的函數
data 由於可能多個定時器調用一個函數,為了使得這個函數能夠區分不同的定時器,
通過在結構中 data 來標識這個定時器,並且通過調用
function( data )
使得 function 能區分它們,也就是 data 起到 ID 的作用。
如何使用
將定時器加到定時器隊列中
void add_timer(struct timer_list *timer)
修改定時器的到期時間
int mod_timer(struct timer_list *timer, unsigned long expires)
將定時器刪除(以后這個定時器將不再起作用)
int del_timer(struct timer_list * timer)
如果不要求很精確的話,用 alarm() 和 signal() 就夠了
代碼:
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void sigalrm_fn(int sig)
{
printf("alarm!\n");
alarm(2);
return;
}
int main(void)
{
signal(SIGALRM,signalrm_fn);
alarm(2);
while(1)
pause();
}
用select()函數可以實現定時,而且可以將時間精確到毫秒級
#include <stdio.h>
#include <time.h>
#include <sys/time.h>
#include <stdlib.h>
#include <signal.h>
int count = 0;
void set_timer()
{
struct itimerval itv, oldtv;
itv.it_interval.tv_sec = 5;
itv.it_interval.tv_usec = 0;
itv.it_value.tv_sec = 5;
itv.it_value.tv_usec = 0;
setitimer(ITIMER_REAL, &itv, &oldtv);
}
void sigalrm_handler(int sig)
{
count++;
printf("timer signal.. %d\n", count);
}
int main()
{
signal(SIGALRM, sigalrm_handler);
set_timer();
while (count < 1000)
{}
exit(0);
}
利用定時器機制實現多線程編程
為了避免Qt系統中多線程編程帶來的問題,還可以使用系統中提供的定時器機制來實現類似的功能。定時器機制將並發的事件串行化,簡化了對並發事件的處理,從而避免了thread-safe方面問題的出現。
在下面的例子中,同時有若干個對象需要接收底層發來的消息(可以通過Socket、FIFO等進程間通信機制),而消息是隨機收到的,需要有一個GUI主線程專門負責接收消息。當收到消息時主線程初始化相應對象使之開始處理,同時返回,這樣主線程就可以始終更新界面顯示並接收外界發來的消息,達到同時對多個對象的控制;另一方面,各個對象在處理完消息后需要通知GUI主線程。對於這個問題,可以利用第3節中的用戶自定義事件的方法,在主線程中安裝一個事件過濾器,來捕捉從各個對象中發來的自定義事件,然后發出信號調用主線程中的一個槽函數。
另外,也可以利用Qt中的定時器機制實現類似的功能,而又不必擔心Thread-safe問題。下面就是有關的代碼部分:
在用戶定義的Server類中創建和啟動了定時器,並利用connect函數將定時器超時與讀取設備文件數據相關聯:
Server:: Server(QWidget *parent) : QWidget(parent)
{
readTimer = new QTimer(this); //創建並啟動定時器
connect(readTimer, SIGNAL(timeout()), this, SLOT(slotReadFile())); //每當定時器超時時調用函數slotReadFile讀取文件
readTimer->start(100);
}
slotReadFile函數負責在定時器超時時,從文件中讀取數據,然后重新啟動定時器:
int Server::slotReadFile() // 消息讀取和處理函數
{
readTimer->stop(); //暫時停止定時器計時
ret = read(file, buf ); //讀取文件
if(ret == NULL)
{ readTimer->start(100); //當沒有新消息時,重新啟動定時器
return(-1);
}
else
根據buf中的內容將消息分發給各個相應的對象處理……;
readTimer->start(100); //重新啟動定時器
}
在該程序中,利用了類似輪循的方式定時對用戶指定的設備文件進行讀取,根據讀到的數據內容將信息發送到各個相應的對象。用戶可以在自己的GUI主線程中創建一個Server類,幫助實現底層的消息接收過程,而本身仍然可以處理諸如界面顯示的問題。當各個對象完成處理后,通過重新啟動定時器繼續進行周期性讀取底層設備文件的過程。當然,這種方法適合於各對象對事件的處理時間較短,而底層設備發來消息的頻率又相對較慢的情況。在這種情況下,上述方法完全可以滿足用戶的需求,而又避免了處理一些與線程並發有關的復雜問題。
