2010年11月11日 星期四

OnTimer是否沒有返回而重複執行

看到一篇blog,http://blog.pfan.cn/yuqiexing/40732.html
/////////////////
CxxxDlg::OnTimer(UINT nIDEvent)
{
  static int i = 0,j;
  j = i++;
  if (i==2) KillTimer(nIDEvent);
  MessageBox("!");
  i++;
  CString str;
  str.Format("%d,%d ",j,i);
  ::OutputDebugString(str);
}
以上代碼在執行的時候,會彈出2個messagebox。很多人都有1個疑問,那就是當第一個消息框彈出的時候,應該相當於是模態對話框,怎麼還會第二次進入OnTimer,並且再次彈出messagebox?
他的解釋是:
原來,當你在主消息循環的消息處理函數中彈出一個模態對話框,他阻塞了主消息循環,理論上,這時可以把主窗口關閉了!但是因為這個彈出的模態對話框還在, 它阻塞了winmain這個主消息循環,實際上你是關不掉的。注意,阻塞的實際意思是說:消息被模態對話框處理了,而沒有傳到後面的父窗口。而每次彈出對 話框,都是由最上層的那個消息框掌握著消息循環,其他的消息循環被阻塞了。

      所以,上面代碼運行的情況是:

(1)彈出第一個messagebox,然後後續代碼暫停執行。

(2)第二次進入OnTimer

(3)彈出第二個messagebox接替消息循環,第一個messagebox禁用。後續代碼暫停執行。

(4)用戶點擊第二個messagebox。此時顯示結果,i = 3,j=1;再次,點擊第一個messagebox,此時顯示結果,i=4 ; j=0
(測試顯示應該是: i =4, j=1);


我的疑問,當彈出第一個messagebox後,主消息循環被阻塞,OnTimer中後續代碼暫停執行,也就是說OnTimer沒有返回,是這樣嗎?那麼 怎麼能夠第二次進入OnTimer呢?這樣的話一個函數還沒有返回又第二次進入執行,有可能造成衝突啊,事實上的確是第二次進入OnTimer了。

以上說明:OnTimer沒有返回時,間隔時間到,可以重複進入該函數。又做了個測試
double fun(double i)//執行時間較長,大於100ms
{
double t = 1;
for(;i <100000000;i++)
{
t = t*i/(i+1);
}
return t;
}

void CMainFrame::OnTimer(UINT nIDEvent)
{
static double i=0,j=0;
fun(i);
j++;
if(j == 0)
{
MessageBox("jjjjjjjjjjjjjjjjjjjjjjjjjj");
}
CFrameWnd::OnTimer(nIDEvent);
}
在主窗口初始化時定義定時器

SetTimer(1,100,NULL);

結果沒有任何messagebox輸出;

也就是說在fun()正在執行,沒有返回時,間隔時間到,並沒有再次進入OnTimer(),而是一直等到fun()返回[b]。[/b]
這個和前一個測試有什麼區別?為什麼能導致不同的結果呢?

---------------------------------------------------------------------
樓主首先要明白,第二次進入OnTimer不需要在第一次退出OnTimer後才能發生,函數重入是非常常見的事情,尤其是有模態對話框的情況,wltg解釋的很好,希望你能懂
引用 6 樓 wltg2001 的回覆:
也就是說OnTimer沒有返回,是這樣嗎?那麼怎麼能夠第二次進入OnTimer呢?
======================
最主要的原因是MessageBox是模態對話框,而模態對話框內部是有消息循環的,當第一個MessageBox彈出時,由於模態對話框內部有消息循環,所以定時器消息還是可以被處理.
你的第一個例子,消息處理程序是阻塞在MessageBox上的,因為MessageBox內部有消息循環,所以消息還是可以被處理的.第二個例子卻是阻塞在fun()上,在這個函數內部卻沒有消息循環,當然要等它做完能繼續下去了.

沒有留言:

張貼留言