由Timer控制項到Windows訊息的思考

2022-03-25 08:27:47 字數 2857 閱讀 7731

問題首先是由vb的timer控制項引起的。大家都知道vb不支援多執行緒,但timer控制項給我們乙個錯覺:乙個timer控

件就是乙個執行緒,timer控制項是並行的。開始我也這麼認為,但一段簡單的**說明了一切。

**如下:

option explicit

dim i as long, j as long

private sub command1_click()

dim x as double

for i = 1 to 1000000

x = sin(2) / i

doevents

next i

if i = 1000001 then msgbox "aaa"

end sub

private sub form_load()

timer1.interval = 10

timer1.enabled = true

end sub

private sub timer1_timer()

if i <> 0 then print "時鐘事件開始:" & i

for j = 1 to 1000

j = j

next j

if i <> 0 then print ",時鐘事件結束:" & i

end sub

執行結果如下:

下面我們分析一下**,在按鈕的單擊事件中,是乙個用來耗時的迴圈(是為了拖延時間,讓我們有充足的時間來檢驗),迴圈中的doevents是在每次迴圈中都轉交控制權,用來檢測是否有其他等待的訊息。if i = 1000001 then msgbox "aaa"是迴圈完成的標誌(順序執行至此表示迴圈已經完成)。在timer事件中也有乙個用來耗時的迴圈,在迴圈的開始和結尾分別放有檢測i值的語句。

我們發現在第一次檢測完i的值後,經歷了乙個特意的耗時迴圈,迴圈完成後再檢測i的值,和迴圈前相同,這說明:時鐘事件程式開始執行,一直到執行結束,原來正在迴圈的程式將停止,即時鐘事件程式開始時原正在執行的迴圈變數值將等於時鐘事件程式結束時的值,即原正在執行的迴圈並沒有繼續迴圈。

如果我們把在按鈕的單擊事件迴圈中的doevents注釋掉,那麼單擊事件(迴圈)就不會交出控制權,那麼直到整個單擊事件結束後才會觸發timer事件,結果如圖:

以上例子證明timer事件不是並行的,而是序列的。在乙個時間內cpu只能處理乙個程式時間片在不同的程式間快速切換,形成了「timer多執行緒」。就好比一名象棋大師,同時要和十個人下棋,十個棋盤一字排開,象棋大師走馬燈一樣在十個棋盤前穿梭落子,直到下完十盤棋局。象棋大師是cpu,十盤棋局是十個執行緒(或程序)任務。

如果我們繼續深究原因的話,就會牽扯到windows訊息。

訊息的迴圈過程大致為(關於訊息的具體情況不再說明)

1. 訊息迴圈呼叫getmessage()從訊息佇列中查詢訊息進行處理,如果訊息隊列為空,程式將停止執行並等待(程式阻塞)。

2. 事件發生時導致乙個訊息加入到訊息佇列(例如系統註冊了乙個滑鼠點選事件),getmessage()將返回乙個正值,這表明有訊息需要被處理,並且訊息已經填充到傳入的msg引數中;當傳入wm_quit訊息時返回0;如果返回值為負表明發生了錯誤。

3. 取出訊息(在msg變數中)並將其傳遞給translatemessage()函式,這個函式做一些額外的處理:將虛擬鍵值資訊轉換為字元資訊。這一步實際上是可選的,但有些地方需要用到這一步。

4. 上面的步驟執行完後,將訊息傳遞給dispatchmessage()函式。dispatchmessage()函式將訊息分發到訊息的目標視窗,並且查詢目標視窗過程函式,給視窗過程函式傳遞視窗控制代碼、訊息、wparam、lparam等引數然後呼叫該函式。

5. 在視窗過程函式中,檢查訊息和其他引數,你可以用它來實現你想要的操作。如果不想處理某些特殊的訊息,你應該總是呼叫defwindowproc()函式,系統將按按預設的方式處理這些訊息(通常認為是不做任何操作)。

6. 一旦乙個訊息處理完成,視窗過程函式返回,dispatchmessage()函式返回,繼續迴圈處理下乙個訊息。

有了這個基礎我們就可以更加本質的看待這個問題

單擊事件中的迴圈每次都要轉交控制權(這個過程是非常快的),所以getmessage()函式得以呼叫,造成getmessage()函式「時時刻刻的存在」。但timer控制項是每10毫秒執行一次(也就是每10毫秒進入一次訊息佇列),所以每10毫秒才可以查詢到這個訊息,順利的完成一次訊息迴圈。這就達到了「timer多執行緒」的效果。仔細觀察可以發現,任意兩個相鄰timer呼叫中i值的增量是大致相同的,更加有力的證明timer的計時一直在進行,每10毫秒進一次佇列。

這給去掉按鈕的單擊事件迴圈中的doevents時的現象乙個良好的鋪墊,這時timer的計時仍然在進行,但由於單擊事件沒有交出控制權,使得getmessage()函式無法呼叫,而這時候dispatchmessage()函式無法返回(因為訊息處理沒有完成),所以timer訊息進了佇列也沒有任何響應,直到單擊事件處理完畢後,timer事件才被觸發。

後記:突然發現有乙個問題沒有說,就是在第一種情況下,為什麼按鈕迴圈心甘情願等timer中的迴圈執行完了再執行呢?因為doevents!按鈕迴圈中有doevents,可以檢測有沒有其他訊息;而timer迴圈中沒有doevents,它會一直霸佔cpu,直到迴圈執行完畢為止,也就是說,乙個時間片不夠,cpu會再給。

控制項timer

通過button控制timer控制項的開啟和暫停,timer控制項開啟時觸發文字框顯示當前時刻,用form1 load設定的時間間隔控制顯示重新整理時間 using system using system.windows.forms namespace 控制項timer private void f...

Timer控制項

timer控制項是定期引發事件的控制項,時間間隔的長度由 interval 屬性定義,其值以毫秒為單位嗎,若啟用了該元件,則每個事件間隔引發乙個 tick 事件,timer 元件的主要方法包括 start 和stop 分別表示開啟和關閉計時器。enable屬性一定要選擇 true datetime....

怎樣使用timer控制項

乙個重要屬性interval,這個值設定每個多長時間執行一次動作,單位是毫秒,還有乙個重要事件是timer,雙擊timer控制項即可自動產生這個事件的框架,在這個框架裡面寫入每隔interval毫秒就要執行的 動作。例如 1.放乙個timer控制項在窗體上 2.設定timer控制項的interval...