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類,幫助實現底層的消息接收過程,而本身仍然可以處理諸如界面顯示的問題。當各個對象完成處理后,通過重新啟動定時器繼續進行周期性讀取底層設備文件的過程。當然,這種方法適合於各對象對事件的處理時間較短,而底層設備發來消息的頻率又相對較慢的情況。在這種情況下,上述方法完全可以滿足用戶的需求,而又避免了處理一些與線程並發有關的復雜問題。