全國最多中醫師線上諮詢網站-台灣中醫網
發文 回覆 瀏覽次數:4387
推到 Plurk!
推到 Facebook!

如何防止重入? (含問題程式)

答題得分者是:hao_chih
dllee
站務副站長


發表:321
回覆:2519
積分:1711
註冊:2002-04-15

發送簡訊給我
#1 引用回覆 回覆 發表時間:2004-05-04 14:17:42 IP:220.139.xxx.xxx 未訂閱
最近在寫程式時,發現原來的防止重入的機制失效了,簡化程式後如下: BCB5 Source with compiled .exe file http://delphi.ktop.com.tw/loadfile.php?TOPICID=15485375&CC=346325
//---------------------------------------------------------------------------
// ReEntry Problem by dllee  <2004-05-04>
//---------------------------------------------------------------------------    #include 
#pragma hdrstop    #include "ReEntryUnit.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
  : TForm(Owner)
{
  Timer1->Enabled=false;
  Timer1->Interval=1000;
  Timer2->Enabled=false;
  Timer2->Interval=3000;
  Timer3->Enabled=true;   // Timer3 是用來判斷 GUI 是否已當掉
  Timer3->Interval=100;      Function1Running=false;
  Function2Running=false;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::TestFunction1(TObject *Sender)
{
  TTimer *timer=dynamic_cast(Sender);
  if(timer!=NULL)
    ListBox1->Items->Add(Now().FormatString("HH:NN:SS ")+timer->Name+" try to use Function1...");      while(Function1Running==true) // 如果前次還沒執行完就再等一下
  {
    Application->ProcessMessages();
    if(CheckBox2->Checked)  Sleep(10);  // 如果需要 Sleep()
  }
  Function1Running=true;        // 開始執行設定旗標
  if(timer!=NULL)
    ListBox1->Items->Add(Now().FormatString("HH:NN:SS ")+timer->Name+" start using Function1...");      int *testMemory=new int[64*1024*1024];  // 取得 64 MB 的記憶體
  for(int i=0;i<64*1024*1024;i++)
  {
    if( CheckBox1->Checked  // 如果有需要 Application->ProcessMessages()
    && i%(1024*1024)==0 )   // 則在此 for loop 內會觸發 64 次
      Application->ProcessMessages();        testMemory[i]=i*i/123;  // 作任意的運算來浪費時間,以模擬真實程式中的各種運算
  }
  delete [] testMemory;      if(timer!=NULL)
    ListBox1->Items->Add(Now().FormatString("HH:NN:SS ")+timer->Name+" finished Function1...");      Function1Running=false;        // 執行完畢清除旗標
}
//---------------------------------------------------------------------------
void __fastcall TForm1::TestFunction2(TObject *Sender)
{
  TTimer *timer=dynamic_cast(Sender);
  if(timer!=NULL)
    ListBox1->Items->Add(Now().FormatString("HH:NN:SS ")+timer->Name+" try to use Function2...");      while(Function2Running==true) // 如果前次還沒執行完就再等一下
  {
    Application->ProcessMessages();
    if(CheckBox2->Checked)  Sleep(10);  // 如果需要 Sleep()
  }
  Function2Running=true;        // 開始執行設定旗標
  if(timer!=NULL)
    ListBox1->Items->Add(Now().FormatString("HH:NN:SS ")+timer->Name+" start using Function2...");      // 叫用三次 TestFunction1
  for(int i=0;i<3;i++)
    TestFunction1(Sender);      if(timer!=NULL)
    ListBox1->Items->Add(Now().FormatString("HH:NN:SS ")+timer->Name+" finished Function2...");      Function2Running=false;        // 執行完畢清除旗標
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
  ListBox1->Items->Add(Now().FormatString("HH:NN:SS ")+"Timer1 Entered");
  TestFunction1(Sender);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Timer2Timer(TObject *Sender)
{
  ListBox1->Items->Add(Now().FormatString("HH:NN:SS ")+"Timer2 Entered");
  TestFunction2(Sender);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
  ListBox1->Items->Add(Now().FormatString("HH:NN:SS ")
    +"Test Started ------------ Timer ON");
  Timer1->Enabled=true;
  Timer2->Enabled=true;      Function1Running=false;
  Function2Running=false;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
  Timer1->Enabled=false;
  Timer2->Enabled=false;
  ListBox1->Items->Add(Now().FormatString("HH:NN:SS ")
    +"Test Finished ----------- Timer OFF");
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Timer3Timer(TObject *Sender)
{ // 此 Timer 用來查看 GUI 是否還可操作
  Label1->Caption=String(Label1->Caption.ToIntDef(0)+1);
}
//---------------------------------------------------------------------------    void __fastcall TForm1::Button3Click(TObject *Sender)
{
  ListBox1->Items->Add(Now().FormatString("HH:NN:SS ")+"Close()");
  Close();
}
//---------------------------------------------------------------------------    void __fastcall TForm1::Button4Click(TObject *Sender)
{
  ListBox1->Items->Add(Now().FormatString("HH:NN:SS ")+"Application->Terminate()");
  Application->Terminate();
}
//---------------------------------------------------------------------------    void __fastcall TForm1::Button5Click(TObject *Sender)
{
  ListBox1->Items->SaveToFile(Now().FormatString("YYYYMMDD_HHNNSS")+".log");  
}
//---------------------------------------------------------------------------
測試如下:
13:13:35 Test Started ------------ Timer ON
13:13:36 Timer1 Entered
13:13:36 Timer1 try to use Function1...
13:13:36 Timer1 start using Function1...
13:13:38 Timer1 finished Function1...
13:13:38 Timer1 Entered
13:13:38 Timer1 try to use Function1...
13:13:38 Timer1 start using Function1...
13:13:39 Timer1 finished Function1...
13:13:39 Timer2 Entered
13:13:39 Timer2 try to use Function2...
13:13:39 Timer2 start using Function2...
13:13:39 Timer2 try to use Function1...
13:13:39 Timer2 start using Function1...
13:13:41 Timer2 finished Function1...
13:13:41 Timer2 try to use Function1...
13:13:41 Timer2 start using Function1...
13:13:43 Timer2 finished Function1...
13:13:43 Timer2 try to use Function1...
13:13:43 Timer2 start using Function1...
13:13:45 Timer2 finished Function1...
13:13:45 Timer2 finished Function2...
13:13:45 Timer1 Entered
13:13:45 Timer1 try to use Function1...
13:13:45 Timer1 start using Function1...
13:13:47 Timer1 finished Function1...
13:13:47 Timer2 Entered
13:13:47 Timer2 try to use Function2...
13:13:47 Timer2 start using Function2...
13:13:47 Timer2 try to use Function1...
13:13:47 Timer2 start using Function1...
13:13:49 Timer2 finished Function1...
13:13:49 Timer2 try to use Function1...
13:13:49 Timer2 start using Function1...
13:13:51 Timer2 finished Function1...
13:13:51 Timer2 try to use Function1...
13:13:51 Timer2 start using Function1...
13:13:53 Timer2 finished Function1...
13:13:53 Timer2 finished Function2...
13:13:53 Timer1 Entered
13:13:53 Timer1 try to use Function1...
13:13:53 Timer1 start using Function1...
13:13:55 Timer1 finished Function1...
13:13:55 Timer2 Entered
13:13:55 Timer2 try to use Function2...
13:13:55 Timer2 start using Function2...
13:13:55 Timer2 try to use Function1...
13:13:55 Timer2 start using Function1...
13:13:56 Timer2 finished Function1...
13:13:56 Timer2 try to use Function1...
13:13:56 Timer2 start using Function1...
13:13:58 Timer2 finished Function1...
13:13:58 Timer2 try to use Function1...
13:13:58 Timer2 start using Function1...
13:14:00 Timer2 finished Function1...
13:14:00 Timer2 finished Function2...
13:14:00 Timer1 Entered
13:14:00 Timer1 try to use Function1...
13:14:00 Timer1 start using Function1...
13:14:02 Timer1 finished Function1...
13:14:02 Timer2 Entered
13:14:02 Timer2 try to use Function2...
13:14:02 Timer2 start using Function2...
13:14:02 Timer2 try to use Function1...
13:14:02 Timer2 start using Function1...
13:14:04 Timer2 finished Function1...
13:14:04 Timer2 try to use Function1...
13:14:04 Timer2 start using Function1...
13:14:06 Timer2 finished Function1...
13:14:06 Timer2 try to use Function1...
13:14:06 Timer2 start using Function1...
13:14:08 Timer2 finished Function1...
13:14:08 Timer2 finished Function2...
13:14:08 Test Finished ----------- Timer OFF
13:14:28 Test Started ------------ Timer ON
13:14:29 Timer1 Entered
13:14:29 Timer1 try to use Function1...
13:14:29 Timer1 start using Function1...
13:14:30 Timer1 Entered
13:14:30 Timer1 try to use Function1...
13:14:31 Timer2 Entered
13:14:31 Timer2 try to use Function2...
13:14:31 Timer2 start using Function2...
13:14:31 Timer2 try to use Function1...
13:14:31 Timer1 Entered
13:14:31 Timer1 try to use Function1...
13:14:32 Timer1 Entered
13:14:32 Timer1 try to use Function1...
13:14:33 Timer1 Entered
13:14:33 Timer1 try to use Function1...
13:14:34 Timer2 Entered
13:14:34 Timer2 try to use Function2...
13:14:34 Timer1 Entered
13:14:34 Timer1 try to use Function1...
13:14:35 Timer1 Entered
13:14:35 Timer1 try to use Function1...
13:14:36 Timer1 Entered
13:14:36 Timer1 try to use Function1...
13:14:37 Timer2 Entered
13:14:37 Timer2 try to use Function2...
13:14:37 Timer1 Entered
13:14:37 Timer1 try to use Function1...
13:14:38 Timer1 Entered
13:14:38 Timer1 try to use Function1...
13:14:39 Timer1 Entered
13:14:39 Timer1 try to use Function1...
13:14:40 Timer2 Entered
13:14:40 Timer2 try to use Function2...
13:14:40 Timer1 Entered
13:14:40 Timer1 try to use Function1...
13:14:41 Timer1 Entered
13:14:41 Timer1 try to use Function1...
13:14:42 Timer1 Entered
13:14:42 Timer1 try to use Function1...
13:14:43 Timer2 Entered
13:14:43 Timer2 try to use Function2...
13:14:43 Timer1 Entered
13:14:43 Timer1 try to use Function1...
13:14:44 Timer1 Entered
13:14:44 Timer1 try to use Function1...
13:14:45 Timer1 Entered
13:14:45 Timer1 try to use Function1...
13:14:46 Timer2 Entered
13:14:46 Timer2 try to use Function2...
13:14:46 Timer1 Entered
13:14:46 Timer1 try to use Function1...
13:14:47 Timer1 Entered
13:14:47 Timer1 try to use Function1...
13:14:48 Test Finished ----------- Timer OFF
13:14:56 Close()
13:15:00 Application->Terminate()
13:15:04 Close()
在 13:13:35 按下 [Timer ON] 按鈕開始測試,此時,當 Timer1/2 執行時,左上方的計數器就幾乎是當掉。而此時 Timer1/2 執行的頻率變成一樣,即使設定上有 3 倍的差距。 在 13:14:08 按下 [Timer OFF] 按鈕,結束第一次測試。結果基本上 OK 只是 GUI 等於是當掉。 在 13:14:28 按下 [Timer ON] 同時勾選 Using Application->ProcessMessages() in for loop,結果是左上方的計數器可以正常運作,但是 Timer1/2 會重入而且都作不完,此時的 CPU Loading 為 100%,再勾選 Using Sleep(10) in while loop,CPU Loading 降為 0%,由此可知,程式都死在 while loop 內不出來... 在這種狀況下,按下 [Close] 按鈕 或按下 [Application->Terminate()] 按鈕,程式都不會結束,只能叫用工作管理員來刪除它... 請問,是否有什麼方法可以防止重入,同時,在函式內還可以使用 Application->ProcessMessages() 呢? 沒空更新的網頁... http://dllee.ktop.com.tw C及指標教學,計算機概論,資訊管理導論... http://dllee.adsldns.org 介紹Shells,LiteStep,GeoShell....
------
http://www.ViewMove.com
hao_chih
一般會員


發表:15
回覆:25
積分:18
註冊:2003-09-10

發送簡訊給我
#2 引用回覆 回覆 發表時間:2004-05-04 18:07:27 IP:61.63.xxx.xxx 未訂閱
dllee版主您好,給你些意見,還請您參考參考 我想你的程式會重入或死當的最主要原因是旗標的設置不足。 我想您也知道>>>>>> >多了一個 class="code"> TTimer *timer=dynamic_cast(Sender); if (Reload1) { if(timer!=NULL) ListBox1->Items->Add(Now().FormatString("HH:NN:SS ") timer->Name " double loop in function1..."); return; } if(timer!=NULL) ListBox1->Items->Add(Now().FormatString("HH:NN:SS ") timer->Name " try to use Function1..."); while(Function1Running==true) // 如果前次還沒執行完就再等一下 { Reload1 = true; Application->ProcessMessages(); if(CheckBox2->Checked) Sleep(10); // 如果需要 Sleep() } Reload1 = false; Function1Running=true; // 開始執行設定旗標 =============================================================== 至於你AP沒辦法Terminate的原因,是你還留有執行緒在等待迴圈裡 卻直接的結束掉原本在執行的執行緒,導致函式尚未執行完畢就被終結 (尚未執行到Function1Running=false就被終結)造成Function1Running永遠為true,等待的永遠出不來,死在裡頭關不掉 --------------------------------- 所以在改一下當 class="code"> TTimer *timer=dynamic_cast(Sender); if (Reload2 || onAPClose) { if(timer!=NULL) ListBox1->Items->Add(Now().FormatString("HH:NN:SS ") timer->Name " double loop in function2..."); return; } if(timer!=NULL) ListBox1->Items->Add(Now().FormatString("HH:NN:SS ") timer->Name " try to use Function2..."); while(Function2Running==true) // 如果前次還沒執行完就再等一下 { Reload2 = true; Application->ProcessMessages(); if(CheckBox2->Checked) Sleep(10); // 如果需要 Sleep() if (onAPClose) return; } Reload2 = false; Function2Running=true; // 開始執行設定旗標 ------------------------------------Terminate前,設定旗標
void __fastcall TForm1::Button4Click(TObject *Sender)
{
  onAPClose = true;
  ListBox1->Items->Add(Now().FormatString("HH:NN:SS ") "Application->Terminate()");
  Application->Terminate();
}
 
兩個function都改了以後,經測試ok< > 洋洋灑灑寫了一大串~< >獻醜了 發表人 - hao_chih 於 2004/05/04 18:14:59 發表人 - hao_chih 於 2004/05/04 18:21:03
dllee
站務副站長


發表:321
回覆:2519
積分:1711
註冊:2002-04-15

發送簡訊給我
#3 引用回覆 回覆 發表時間:2004-05-04 22:15:34 IP:211.76.xxx.xxx 未訂閱
感謝 hao_chih 的測試及回應,但是... 這是簡化後的程式,也許,把 Timer 的 Interval 改成一個 Random 的數值更可以展現原有問題。 以目前提供的測試程式,是故意讓一個 Timer 都作不完的狀況下再故意讓另一個 Timer 來攪局。 可以修改程式中 1024*1024 變成 10 就好,也就是由 4*64MB 改成 4*640 Bytes,如此,同一個 Timer 一定可以在其 Interval 內執行完,此時,測試的流程一樣,結果也是一樣。 而原有的問題是不允許重入,而且只要有叫用的,都必需作完,不能因前次還沒作完就不作。    P.S. 原有的問題是,當系統發現 Alarm 時會叫用一個 Alarm 處理的函式,此函式會叫用許多模組來處理這個 Alarm,而每個模組所需的時間不一定相同,而在處理此 Alarm 時,其他的 Alarm 也有可能發生,也需要處理。當然,目前的處理方法,只能把所有的 ProcessMessages() 都刪除不用,以免發生重入而導致軟體異常或當機。而我想知道是否有其他的方法可以防重入,如果允許的話還可以使用 ProcessMessages(),否則,就只能要求所有的開發者都不能用 ProcessMessages()...    沒空更新的網頁... http://dllee.ktop.com.tw C及指標教學,計算機概論,資訊管理導論... http://dllee.adsldns.org 介紹Shells,LiteStep,GeoShell....
------
http://www.ViewMove.com
jest0024
高階會員


發表:11
回覆:310
積分:224
註冊:2002-11-24

發送簡訊給我
#4 引用回覆 回覆 發表時間:2004-05-05 01:16:01 IP:211.74.xxx.xxx 未訂閱
引言: 最近在寫程式時,發現原來的防止重入的機制失效了,簡化程式後如下: BCB5 Source with compiled .exe file http://delphi.ktop.com.tw/loadfile.php?TOPICID=15485375&CC=346325
//---------------------------------------------------------------------------
// ReEntry Problem by dllee  <2004-05-04>
//---------------------------------------------------------------------------    #include 
#pragma hdrstop    #include "ReEntryUnit.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
  : TForm(Owner)
{
  Timer1->Enabled=false;
  Timer1->Interval=1000;
  Timer2->Enabled=false;
  Timer2->Interval=3000;
  Timer3->Enabled=true;   // Timer3 是用來判斷 GUI 是否已當掉
  Timer3->Interval=100;      Function1Running=false;
  Function2Running=false;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::TestFunction1(TObject *Sender)
{
  TTimer *timer=dynamic_cast(Sender);
  if(timer!=NULL)
    ListBox1->Items->Add(Now().FormatString("HH:NN:SS ")+timer->Name+" try to use Function1...");      while(Function1Running==true) // 如果前次還沒執行完就再等一下
  {
    Application->ProcessMessages();
    if(CheckBox2->Checked)  Sleep(10);  // 如果需要 Sleep()
  }
  Function1Running=true;        // 開始執行設定旗標
  if(timer!=NULL)
    ListBox1->Items->Add(Now().FormatString("HH:NN:SS ")+timer->Name+" start using Function1...");      int *testMemory=new int[64*1024*1024];  // 取得 64 MB 的記憶體
  for(int i=0;i<64*1024*1024;i++)
  {
    if( CheckBox1->Checked  // 如果有需要 Application->ProcessMessages()
    && i%(1024*1024)==0 )   // 則在此 for loop 內會觸發 64 次
      Application->ProcessMessages();        testMemory[i]=i*i/123;  // 作任意的運算來浪費時間,以模擬真實程式中的各種運算
  }
  delete [] testMemory;      if(timer!=NULL)
    ListBox1->Items->Add(Now().FormatString("HH:NN:SS ")+timer->Name+" finished Function1...");      Function1Running=false;        // 執行完畢清除旗標
}
//---------------------------------------------------------------------------
void __fastcall TForm1::TestFunction2(TObject *Sender)
{
  TTimer *timer=dynamic_cast(Sender);
  if(timer!=NULL)
    ListBox1->Items->Add(Now().FormatString("HH:NN:SS ")+timer->Name+" try to use Function2...");      while(Function2Running==true) // 如果前次還沒執行完就再等一下
  {
    Application->ProcessMessages();
    if(CheckBox2->Checked)  Sleep(10);  // 如果需要 Sleep()
  }
  Function2Running=true;        // 開始執行設定旗標
  if(timer!=NULL)
    ListBox1->Items->Add(Now().FormatString("HH:NN:SS ")+timer->Name+" start using Function2...");      // 叫用三次 TestFunction1
  for(int i=0;i<3;i++)
    TestFunction1(Sender);      if(timer!=NULL)
    ListBox1->Items->Add(Now().FormatString("HH:NN:SS ")+timer->Name+" finished Function2...");      Function2Running=false;        // 執行完畢清除旗標
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
  ListBox1->Items->Add(Now().FormatString("HH:NN:SS ")+"Timer1 Entered");
  TestFunction1(Sender);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Timer2Timer(TObject *Sender)
{
  ListBox1->Items->Add(Now().FormatString("HH:NN:SS ")+"Timer2 Entered");
  TestFunction2(Sender);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
  ListBox1->Items->Add(Now().FormatString("HH:NN:SS ")
    +"Test Started ------------ Timer ON");
  Timer1->Enabled=true;
  Timer2->Enabled=true;      Function1Running=false;
  Function2Running=false;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
  Timer1->Enabled=false;
  Timer2->Enabled=false;
  ListBox1->Items->Add(Now().FormatString("HH:NN:SS ")
    +"Test Finished ----------- Timer OFF");
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Timer3Timer(TObject *Sender)
{ // 此 Timer 用來查看 GUI 是否還可操作
  Label1->Caption=String(Label1->Caption.ToIntDef(0)+1);
}
//---------------------------------------------------------------------------    void __fastcall TForm1::Button3Click(TObject *Sender)
{
  ListBox1->Items->Add(Now().FormatString("HH:NN:SS ")+"Close()");
  Close();
}
//---------------------------------------------------------------------------    void __fastcall TForm1::Button4Click(TObject *Sender)
{
  ListBox1->Items->Add(Now().FormatString("HH:NN:SS ")+"Application->Terminate()");
  Application->Terminate();
}
//---------------------------------------------------------------------------    void __fastcall TForm1::Button5Click(TObject *Sender)
{
  ListBox1->Items->SaveToFile(Now().FormatString("YYYYMMDD_HHNNSS")+".log");  
}
//---------------------------------------------------------------------------
測試如下:
    請問,是否有什麼方法可以防止重入,同時,在函式內還可以使用 Application->ProcessMessages() 呢?    沒空更新的網頁...
http://dllee.ktop.com.tw   C及指標教學,計算機概論,資訊管理導論... http://dllee.adsldns.org  介紹Shells,LiteStep,GeoShell....

首先,我是學Delphi的,不太懂的如何寫C++,在此只獻上我的想法
  當程式使用
  Application->ProcessMessages()
  時會繼續接收訊息而觸發Timer事件,"好想用畫的把概念畫出來"
  //不太會寫了,把我想如何做的寫出好了 
  就是使用TList來放置執行函數,讓目前的函式只能執行"1"個..    Type TProc = procedure; //設定TProc為一個函數的指標
var
  fList:TList;
  fRun :Boolean = False;
  fAdd :Boolean = False;
 
//當Timer觸發時先經由TimerAdd函數來判斷是否要執行函式
//如果函式在執行時,先將指標加入List中,如果執行完畢從List讀出第1個
//指標執行。    procedure TimerAdd(Index:Integer);
var p:TProc;
begin
  fAdd:=True;
  p:=nil;
  Case Index of
    0:p:=TestFunction1;
    1:p:=TestFunction2;
  end;
  if p<>nil then begin
    fList.Add(@p);//加入欲要執行的函數
    if(Not fRun)and(fList.Count>0)then begin //當fRun沒在執行時才動作
      p:=fList[0];
      fList.Delete(0);
      p;  //執行函式
    end;
  end;
  fAdd:=False;
end;    procedure TestFunction1;
begin
 fRun:=True;
 {執行程式}
 fRun:=False; 
end;    procedure TestFunction2;
begin
  fRun:=True;
  //如果須要呼叫Application.ProcessMessages時,
  //先判斷TimerAdd是否在執行,這樣才不會讓TimerAdd執行到一半跳出去
  if(Not fAdd)then Application.ProcessMessages;
  {執行程式}
  fRun:=False;
end;
//希望會有幫助...
學而時習之不亦樂乎! 發表人 - jest0024 於 2004/05/05 02:16:13
jest0024
高階會員


發表:11
回覆:310
積分:224
註冊:2002-11-24

發送簡訊給我
#5 引用回覆 回覆 發表時間:2004-05-05 01:17:06 IP:211.74.xxx.xxx 未訂閱
ps.跳槽到這邊,拍謝,希望有幫助...不太會打C  的函數,如看不懂,我在貼上C  的吧!!..{目前沒裝C   Builder}
丫,多貼了...@@"
會造成重入是因為多呼叫了多次的函數..    function Test;
begin
 fRun:=True; 
 while true do {loop};
 fRun:=False;
end;
//上方不加入Application.ProcessMessages;時所以Timer不會觸發,但畫面因為無法接收訊息也不會重繪!!    function Test;
begin
  fRun:=True;
  while true do begin
    {loop}
    Application.ProcessMessages; 
    //因為打上這句會造成訊息接收,而使程式回到Test的初始,造成fRun不能設為False了!!所以重入也無效了!
  end;
  fRun:=False;
end;
發表人 - jest0024 於 2004/05/05 01:24:48
dllee
站務副站長


發表:321
回覆:2519
積分:1711
註冊:2002-04-15

發送簡訊給我
#6 引用回覆 回覆 發表時間:2004-05-05 09:13:48 IP:220.139.xxx.xxx 未訂閱
感謝 jest0024 的回應,個人對於 Delphi 也只能看懂一些,不過,只要觀念說得出來,都可以理解的。    按您的建議,是我可以自行維護一個 TList 來存要執行的函式指標,以我原有問題,等於是每發一個 Alarm 或 Event 就將它存到 TList 中,另外,再交由一個 Timer 每固定時間由該 TList 取出一個來執行,如果前一個還沒執行完,就先不執行,如此,等一個執行完,下次 Timer 再觸發時,再由該 TList 取出一個來執行。這樣似乎可行,我再試試看。    如果有其他的方法,也歡迎提出來,大家討論一下,Delphi 也行  <>沒空更新的網頁... href="http://dllee.adsldns.org">http://dllee.adsldns.org 介紹Shells,LiteStep,GeoShell....
------
http://www.ViewMove.com
hao_chih
一般會員


發表:15
回覆:25
積分:18
註冊:2003-09-10

發送簡訊給我
#7 引用回覆 回覆 發表時間:2004-05-05 09:56:46 IP:61.63.xxx.xxx 未訂閱
抱歉沒看清楚你的問題< > 如果多執行緒縮成一個,那似乎只有用 >< >
dllee
站務副站長


發表:321
回覆:2519
積分:1711
註冊:2002-04-15

發送簡訊給我
#8 引用回覆 回覆 發表時間:2004-05-05 12:03:58 IP:220.139.xxx.xxx 未訂閱
引言: 抱歉沒看清楚你的問題< > 如果多執行緒縮成一個,那似乎只有用 >< > 您客氣了 < > 是我自己問題簡化的不好 < > 對於您提到優先順序及 Suspend/Resume 的方式可否再多作說明呢? 因為我不太了解這部分... 麻煩您了。 沒空更新的網頁... http://dllee.ktop.com.tw C及指標教學,計算機概論,資訊管理導論... http://dllee.adsldns.org 介紹Shells,LiteStep,GeoShell.... 發表人 - dllee 於 2004/05/05 12:10:55
------
http://www.ViewMove.com
jest0024
高階會員


發表:11
回覆:310
積分:224
註冊:2002-11-24

發送簡訊給我
#9 引用回覆 回覆 發表時間:2004-05-05 13:02:07 IP:211.74.xxx.xxx 未訂閱
你好,我又來了..^^!! void __fastcall TForm1::TestFunction1(TObject *Sender) {   if(isRun){ //這行一直等待isRun為False Application->ProcessMessages(); //收到WM_TIMER訊息,程式回到TestFunction1 } isRun = True; While(True){ {Loop code} Application->ProcessMessages(); //收到WM_TIMER訊息,程式回到TestFunction1 } isRun = False; //^--這行一直執行不到 } 這種寫法好像遞回...無窮的遞回,執行到最後會造成記憶體溢滿 使用執行緒,是這樣子嗎!??哇係初學者..~"~!! procedure TMyThread.Execute; begin 想要的執行重作 於執行完畢釋放執行緒 end; procedure TestFunction1; begin 建立執行緒 設定執行緒优先順序 開始執行執行緒 end; procedure Timer; begin 定時呼叫TestFunction1 end; 學而時習之不亦樂乎! 發表人 - jest0024 於 2004/05/05 13:05:51
dllee
站務副站長


發表:321
回覆:2519
積分:1711
註冊:2002-04-15

發送簡訊給我
#10 引用回覆 回覆 發表時間:2004-05-05 13:51:33 IP:220.139.xxx.xxx 未訂閱
說真的,我對執行緒不是很懂  我對執行緒最最不解的就是,系統如何去分割執行緒的執行呢? 如:<>< face="Verdana, Arial, Helvetica">引言:procedure TMyThread.Execute; begin 想要的執行重作 於執行完畢釋放執行緒 end; 想要的執行重作的部分如果需要時間較長,系統是如何判斷目前的執行緒可以先中斷,切到別的執行緒後再回來繼續作呢? 這個切換點有沒有特徵呢?還是由系統任意決定?還是在 loop 內就一定要作完才切換?或是... 我實在是不懂 以此問題所提供的範例程式,我可以知道在 >>一起執行,這樣就重入了,不行。 另外,如果用多執行緒不同優先權來控制,如第一次叫用優先權最高,之後漸減,則到最後就不知如何再設定優先權了。此外,如同之前所說的,當較高優先權的執行緒在執行時,<>優先權比它低一定不會執行嗎? 如果也會執行或有可能執行,也一樣是重入了。 <>沒空更新的網頁... href="http://dllee.adsldns.org">http://dllee.adsldns.org 介紹Shells,LiteStep,GeoShell....
------
http://www.ViewMove.com
lu
高階會員


發表:11
回覆:189
積分:195
註冊:2003-11-19

發送簡訊給我
#11 引用回覆 回覆 發表時間:2004-05-05 14:32:24 IP:221.169.xxx.xxx 未訂閱
在想要的執行重作的部分如果需要時間較長,系統是如何判斷目前的執行緒可以先中斷,切到別的執行緒後再回來繼續作呢? 這一部份是系統的核心功能,一般來說一個好的核心系統,不會其中一隻程式當掉,導致整個程式當掉(有迷有粉熟悉的感覺,沒錯,這就是WIN95、98最大的缺點....之一啦) 這個切換點有沒有特徵呢?還是由系統任意決定?還是在 loop 內就一定要作完才切換?或是... 我實在是不懂 YES 系統決定,一般來說是每隔一小段時間就切換一次,當然該程式所能獲得多少CPU時間,由執行緒的優先權決定之 以此問題所提供的範例程式,我可以知道在 Application->ProcessMessages() 是切換點,但是在多執行緒的狀況下,就不清楚切換點在那兒了... 有件事,偶粉好奇,這支程式...應該不式多執行緒程式喔,你可以在BCB執行時按Ctrl + Alt + T 看看目前停在哪一個THREAD,至於Application->ProcessMessages()的功能,跟多執行緒沒有關係喔,請看看說明 如果按您新的方法,叫用 TestFunction1 就開執行緒,可能會發生兩個以上的執行緒一起執行,這樣就重入了,不行。 另外,如果用多執行緒不同優先權來控制,如第一次叫用優先權最高,之後漸減,則到最後就不知如何再設定優先權了。此外,如同之前所說的,當較高優先權的執行緒在執行時,優先權比它低一定不會執行嗎? 如果也會執行或有可能執行,也一樣是重入了。 所謂多執行緒絕對不是用bool 的變數,來解決所謂的重入或是鎖定的問題喔(這是標準錯誤的方式喔),多執行緒基本上來說是同一個時間可以做多件事,例如一邊接收WINSOCKET的資料,同一時間,一邊將資料寫入檔案,然後還有時間處理UI喔,舉各例子,上面這支程式,如果把Application->ProcessMessages()拿掉,那麼UI就完全停擺了喔 以下是Application->ProcessMessages()的說明 Call ProcessMessages to permit Windows to process these messages that are currently in the message queue. ProcessMessages cycles the Windows message loop until it is empty and then returns control to the application. 這是讓程式去處理一下MESSAGE QUERY理的MESSAGE喔,跟多執行緒沒有關係喔 最後關於多執行緒的問題已經脫離本問題太遠了,就此打住,如有問題,請再新開問題 BYE BYE ========================= 大家一起快樂寫程式
dllee
站務副站長


發表:321
回覆:2519
積分:1711
註冊:2002-04-15

發送簡訊給我
#12 引用回覆 回覆 發表時間:2004-05-05 22:48:29 IP:211.76.xxx.xxx 未訂閱
感謝 lu 的指教。 沒錯,多執行緒的問題確實離題太遠了  我知道 >>>< face="Verdana, Arial, Helvetica">引言: 所謂多執行緒絕對不是用bool 的變數,來解決所謂的重入或是鎖定的問題喔(這是標準錯誤的方式喔) 在原有的程式中,我也使用了 CRITICAL_SECTION,但是又因為多層叫用不同的函式,只要有一層可能有 Application->ProcessMessages() 就會讓 CRITICAL_SECTION 失效,導致重入而資料錯亂。當然目前已把所有的 Application->ProcessMessages() 都幹掉,只是希望知道是否有其他防止重入的方法,仍可以使用 Application->ProcessMessages(),如此,在多人共同開發時,就不需要要求太多,否則,就是將這個天條加入。 對於多執行緒似乎您有很好的解決重入或鎖定的方法,已另外提問: ■【問題】如何解決多執行緒重入及鎖定的問題 http://delphi.ktop.com.tw/topic.php?TOPIC_ID=49583 希望 lu 能多多指教,謝謝。 沒空更新的網頁... http://dllee.ktop.com.tw C及指標教學,計算機概論,資訊管理導論... http://dllee.adsldns.org 介紹Shells,LiteStep,GeoShell.... 發表人 - dllee 於 2004/05/05 23:00:03
------
http://www.ViewMove.com
conundrum
尊榮會員


發表:893
回覆:1272
積分:643
註冊:2004-01-06

發送簡訊給我
#13 引用回覆 回覆 發表時間:2004-05-06 00:53:27 IP:61.64.xxx.xxx 未訂閱
如 何 使 用 Delphi 開 發 大 型 主 從 架 構 系 統 - Package 的 秘 密 和 威 力 (續)  http://www2.borland.com.tw/tw/reference9-4.html 如 何 使 用 Delphi 開 發 大 型 主 從 架 構 系 統 - Package 的 秘 密 和 威 力 (續)    加 入 多 執 行 緒 載 入 的 能 力    在 上 一 節 中 你 看 到 了 使 用 Package 的 確 可 以 大 幅 加 快 子 系 統 載 入 的 時 間 。 但 是 我 們 在 載 入 主 程 式 時 仍 然 需 要 20 到 30 秒 的 時 間 , 因 為 主 程 式 還 是 需 要 載 入 共 用 的 資 料 模 組 Package 。 但 是 對 於 許 多 案 子 來 說 , 使 用 者 經 常 會 要 求 程 式 必 須 在 10 秒 或 是 5 秒 之 內 第 一 個 畫 面 就 必 須 出 現 。 那 麼 對 於 這 種 要 求 我 們 可 不 可 以 達 到 呢 ?    如 果 你 想 到 Delphi 中 的 多 工 執 行 緒 功 能 Tthread 物 件 的 話 , 那 麼 答 案 就 出 現 了 。 我 們 只 需 要 在 主 程式 第 一 個 表 格 出 現 之 時 , 啟 動 一 個 多 執 行 緒 物 件 讓 它 負 責 載 入 公 用 的 資 料 模 組 不 就 可 以 了 嗎 ? 如 此 一 來 主 表 格 可 以 立 刻 的 出 現 在 螢 幕 之 上 , 而 讓 執 行 緒 在 背 景 繼 續 的 載 入 資 料 模 組 。    為 了 要 加 入 多 執 行 緒 的 能 力 , 你 必 須 修 改 上 一 節 的 範 例 。 在 範 例 程 式 的 FormActivate 事 件 處 理 函 數 我 們 不 再 直 接 呼 叫 LoaddbPackage , 而 是 建 立 一 個 Tthread 物 件 , 然 後 由 這 個 Tthread 物 件 真 正 的 載 入 資 料 模 組 Package 。     
 procedure TMainForm.FormActivate(Sender: TObject);
begin 
DBThread := TOracleThread.Create(True);
aDBThread.OnTerminate := dbThreadTerminated;
aDBThread.Resume;    end;
在 TOracleThread 的 Execute 虛 擬 方 法 中 , 它 呼 叫 了 LoaddbPackage 載 入 資 料 模 組 。 在 這 裡 有 一 點 非 常 重 要 的 地 方 便 是 當 你 在 執 行 緒 中 載 入 Package 時 , 所 有 的 handle 值 和 物 件 參 考 值 必 須 儲 存 在 主 執 行 緒 的 變 數 之 中 。 否 則 在 這 個 執 行 緒 執 行 完 畢 之 後 , 這 些 handle 值 和 物 件 參 考 值 都 會 成 為 無 效 的 數 值 。 所 以 在 下 面 的 程 式 碼 中 你 可 以 看 到 執 行 緒 在 呼 叫 LoadPackage 載 入 Package 時 會 把 LoadPackage 回 傳 的 數值 儲 存 在 主 表 格 的 變 數 之 中 。
 procedure TOracleThread.Execute;
begin 
{ Place thread code here }
LoaddbPackage;    end;    procedure TOracleThread.LoaddbPackage;
begin    //我們必須載入資料庫Package以便連結到資料庫
try    MainForm.aDBConnect := LoadPackage(DBPackages);
LoadDataModule;    except    on E : Exception do
begin    MessageBeep(Word(-1));
ShowMessage(E.Message);
Application.Terminate;    end;    end;    end;    procedure TOracleThread.LoadDataModule;
var    iCounter : Integer;    begin    { Note that TApplication "owns" this form and thus it must be freed prior
to unloading the package }
dataModuleClass := GetClass('TConcreteDataModule');
if dataModuleClass <> nil then
begin    MainForm. admGlobal:= TDataModule(TComponentClass(dataModuleClass).Create(Application));
end;    end;
當 我 們 修 改 完 範 例 程 式 之 後 , 就 可 以 試 著 再 次 的執 行 它 , 看 看 主 程 式 的 載 入 時 間 是 不 是 有 任 何 的 改 善 。 下 面 的 表 格 和 圖 形 顯 示 出 當 我 們 使 用 了 執行 緒 的 功 能 之 後 , 第 一 支 主 程 式 啟 動 的 時 間 果 然大 幅 減 少 為 3 秒 鐘 。 比 起 原 來 的 20 秒 果 然 改 善 了 許 多 , 真 是 令 人 印 象 深 刻 。 圖 十 三 加 入 執 行 緒 後 應 用 程 式 執 行 的 效 率 現 在 使 用 了 執 行 緒 功 能 之 後 , 不 但 每 一 個 子 系 統啟 動 的 時 間 加 快 了 許 多 , 主 程 式 更 是 可 以 在 瞬 間出 現 , 這 樣 的 執 行 速 度 應 該 是 可 以 讓 大 部 份 的 人滿 意 了 吧 。 上 頁 / 下 頁 看看 不知道是否有用
dllee
站務副站長


發表:321
回覆:2519
積分:1711
註冊:2002-04-15

發送簡訊給我
#14 引用回覆 回覆 發表時間:2004-05-06 09:37:00 IP:220.139.xxx.xxx 未訂閱
to conundrum: 感謝您提供的資訊  提醒您 > >大改版... 您介紹的這個 >沒空更新的網頁... href="http://dllee.adsldns.org">http://dllee.adsldns.org 介紹Shells,LiteStep,GeoShell....
------
http://www.ViewMove.com
lu
高階會員


發表:11
回覆:189
積分:195
註冊:2003-11-19

發送簡訊給我
#15 引用回覆 回覆 發表時間:2004-05-06 11:19:44 IP:221.169.xxx.xxx 未訂閱
偶也來貢獻一下,以下是 > ========================= <>大家一起快樂寫程式
jest0024
高階會員


發表:11
回覆:310
積分:224
註冊:2002-11-24

發送簡訊給我
#16 引用回覆 回覆 發表時間:2004-05-06 12:32:40 IP:211.74.xxx.xxx 未訂閱
總而言之,言而總之,"單一執行緒"應該不存在函數"重入"的問題.. 若是存在的話,那是程式本身就有問題了!!    而使用多執行緒,在重要程式區段加入互斥元、旗標或事件來判斷程式區段或 資料的"單執行緒或多執行緒"執行    學而時習之不亦樂乎!    無聊閑晃了... 發表人 - jest0024 於 2004/05/06 14:31:50
lu
高階會員


發表:11
回覆:189
積分:195
註冊:2003-11-19

發送簡訊給我
#17 引用回覆 回覆 發表時間:2004-05-06 14:00:23 IP:221.169.xxx.xxx 未訂閱
引言: 總而言之,言而總之,"單一執行緒"應該不存在函數"重入"的問題.. 若是存在的話,那是程式本身就有問題了!! 而使用多執行緒,在重要程式區段加入互斥元、旗標或事件來判斷程式區段或 資料的"單執行緒或多執行緒"執行
YES 基本上來說,"單一執行緒"應該不存在函數"重入"的問題,但是有『遞迴』的問題喔,舉各例子
void main()
{
puts("Test...");
TestFunction()
}    voif TestFunction()
{
int tt;
static int Count=0;
if (Count >= 3)
  return;
tt = Count;
printf ("Start Test Count=%d\n",Count  );
TestFunction();
printf ("End Test Count=%d",tt);
}
以上的程式各位試著執行一下就知道了,基本上偶覺得dllee大大的範例程式,應該是屬於『遞迴』的問題喔,要認定粉簡單,各位在程式執行時,將程式暫停然後按 Ctrl+Alt+S 叫出Stack 來觀看...各位會發現,TestFunction() 重複執行好幾次 而Multi Thread基本上是使用Event , Mutex ,CritionSection等方式來進行鎖定或同步.... 偶個人建議能不寫Multi Thread 就不寫Multi Thread程式,原因無他....因為Multi Thread寫起來粉麻煩...光是資料鎖定、程式同步的問題,就夠頭痛的,還有所謂的死結問題,更甚者...在THREAD中,基本上不能使用多數的VCL物件、檔案的操作(檔案不是不行用,只是要..動點手腳),所以寫蠻麻煩的,而且DEBUG不容易.... 偶建議如果一定要寫MULTI THREAD程式,就先用最保險、最安全的方式來寫(當然這樣會犧牲效率),等程式穩定了,再進行修改....會比較好 ========================= 大家一起快樂寫程式
dllee
站務副站長


發表:321
回覆:2519
積分:1711
註冊:2002-04-15

發送簡訊給我
#18 引用回覆 回覆 發表時間:2004-05-06 17:52:30 IP:220.139.xxx.xxx 未訂閱
感謝 lu 提供的範例,我會再試試  另外,經過多次的討論,我想,結論應該是如 > > < > <>沒空更新的網頁... href="http://dllee.adsldns.org">http://dllee.adsldns.org 介紹Shells,LiteStep,GeoShell....
------
http://www.ViewMove.com
conundrum
尊榮會員


發表:893
回覆:1272
積分:643
註冊:2004-01-06

發送簡訊給我
#19 引用回覆 回覆 發表時間:2004-05-06 18:54:44 IP:61.221.xxx.xxx 未訂閱
http://www.comstory.com/Palm/15days/1-2eventsconcept.htm    1-2 事件驅動觀念    基本觀念    Palm與許多其它的應用程式一樣, 是採用事件驅動的方式來撰寫程式, 如果你曾經用過VB, VFP, VC, Delphi, 你應該對事件驅動的觀念並不陌生, 因為這些程式都是採用事件驅動的方式來開發程式.     什麼是事件驅動呢? 簡單來說, 當外界對Palm作了任何的動作後, 都會透過硬體的插斷傳入一個訊號給作業系統Palm OS, 而OS收到訊息後, 就會產生一個事件. 當然了, 事件並不一定是完全由硬體觸發產生, 也有可能由軟體產生一個事件. 舉例來說, 當觸筆點到螢幕上時, 會有硬體插斷產生一個PenDown的事件, 而PenDown的事件, 交給作業系統後, 可能會另外產生memuDown事件.    透過事件的產生, 我們在程式中, 只要透過解析事件, 即可依據事件的狀態, 決定下一步動作. 這也就形成了程式開發的基礎.    事件迴圈     在傳統的程式中, 通常在程式啟動完成後, 都會加一行 read events的指令, 一般在高階語言中, 有時候並不真正知道這行程式的用意, 只知道要寫下此行程式, 軟體才能順利執行. 其實 read events是事件驅動程式裡面一個很重要的觀念, 它就是讓程式進入一個無窮迴圈裡面, 不斷地去偵側事件的產生, 然後再依據產生的事件, 去回應事件的動作.    迴圈裡面究道是作什麼事呢? 其實它就是一組 Do while ( Loop ) 的語法, 如果迴圈裡面, 不作任何處理, 則程式一旦進入, 就永遠不會出來, 因為沒有一個適當的出口給它. 當然了, 沒有人會這樣寫程式, 讓程式跳不出來, 所以我們會在迴圈裡面寫一個判斷式, 只要遇到某一種狀況, 就要離開此迴圈, 然後讓程式順利結束. 通常這種狀況是發生在, 當使用者按了某個結束鈕後, 由此鈕的程式中, 去產生一個結束事件, 然後在迴圈中的判斷式中, 寫到當某個事件值等於結束事件時, 就跳出迴圈.     Palm程式的寫法, 也不脫離這樣的觀念, 它也是在程式啟動完成後, 即進入無窮迴圈中, 並等待結束事件產生後, 則離開程式. 但由於Palm沒有結束程式的觀念, 因此它並不像Windows程式一樣, 要有一個結束按鈕, 或在功能表中有一個結束的項目, 用來離開程式. Palm本身因為是單工的作業系統, 因此一次只能作一件事, 所以無法開發多執行緒的程式, 所以它所謂的程式結束, 其實是只要其它的程式一開始, 就是上個程式的結束. 因此在事件迴圈中, 系統會自動偵測到結束事件, 而我們的程式因收到一個結束事件, 而跳出迴圈.     事件迴圈程式    static void AppEventLoop (void)     { Word error;     EventType event;      do {      EvtGetEvent (&event, evtWaitForever);     PreprocessEvent (&event);     if (! SysHandleEvent (&event))      if (! MenuHandleEvent (NULL, &event, &error))     if (! ApplicationHandleEvent (&event))     FrmDispatchEvent (&event);      }      while (event.eType != appStopEvent);     }     對於每個 Palm 程式而言,它一定有一個 PilotMain 函式作為開始,如下範例:    DWord PilotMain( Word cmd, Ptr cmdPBP, Word launchFlags)    {       return StarterPilotMain(cmd, cmdPBP, launchFlags);     }     我們會在 PilotMain 函式裡面,呼叫一個我們自訂的 StarterPilotMain 函式,然後從 StarterPilotMain 函式開始進入我們的程式.究竟上述的 AppEventLoop 函式要寫在那裡呢?其實就是寫在 StarterPilotMain 函式中,而且是當它處理完了所有程式的起始動作後,然後呼叫此 AppEventLoop 函式,開始進入事件迴圈中.在 5-1 開始一個範例程式中,會有一個標準的範例程式範本.    接著,我們就來看看這個事件迴圈是如何處理所有的事件.    一開始的 do { ... } while (event.eType != appStopEvent); 動作,是表示不斷地重覆執行迴圈,直到接收到一個appStopEvent事件,才結束,也就是迴圈的出口.什麼時候系統會產生此事件呢?一般是當你啟動另外一個程式的時候,appStopEvent事件會被觸發,然後你的程式也隨之結束.    一旦進入事件迴圈,第一件事就是透過 EvtGetEvent (&event, evtWaitForever); 抓取事件值,並放在 event 變數中,接下來進行 PreprocessEvent (&event); 這是作一些事件的準備動作,由你自己去完成,所以本函式可有可無,不一定需要.    接著連續執行下列動作,當其中一段不成立時,表示接收到的事件,在此函式中沒有或不需被處理,因此接著進行下一個函式來處理此事件.    if (! SysHandleEvent (&event))      if (! MenuHandleEvent (NULL, &event, &error))     if (! ApplicationHandleEvent (&event))     FrmDispatchEvent (&event);     我們會透過參數傳遞,將我們接收到的事件,傳入給事件處理函式來處理它.什麼是事件處理函式呢?凡是xxxHandleEvent( ) 種類的函式,我們統稱事件處理函式,意思是指本函式是要用來處理事件規則的函式.    SysHandledEvent( ) ; 處理系統預設事件,像是硬體方面的事件,如 Turn on , Turn off 等.    MenuHandleEvent( ) ; 處理目前功能表的事件,當你點到功能表位置時,產生的事件,交由它處理.    ApplicationHandleEvent( ) ; 當傳入的事件不是系統事件,也不是功能表事件時,則呼叫應用程式事件函式,交由它來處理事件.    FrmDispatchEvent( );將事件導引到 Form 的事件處理函式來處理.意思就是說,經過上面三個函式都無法處理的事件,則需要透過目前的 Form 事件處理函式,來處理它.本函式的作用,就是會呼叫目前的 Form 事件處理函式.    前三個事件處理函式都比較容易理解,最後一個 FrmDispatchEvent 就比較難懂.其實,簡單地說,前二個事件處理函式,主要是處理系統本身的訊息,我們不太需要管它,而第三個程式事件處理函式,就是我們自己要寫的.    第三個事件處理函式,我們稱它為應用程式事件處理函式,一個 Palm 程式僅需要一個應用程式事件處理函式即可,一般命名為 ApplicationHandleEvent( ).它的功能僅是很簡單地偵側到目前是那個 Form 被啟動,然後,用 FrmSetEventHandler( ) 來設定目前 Form 的事件處理函式,設定好了後,當執行到 FrmDispatchEvent( ) 函式時,它就會呼叫目前 Form 的事件處理函式.    來看看 ApplicationHandleEvent( ) 它的寫法:    static Boolean ApplicationHandleEvent( EventPtr eventP)    {       Word formId;       FormPtr frmP;       if (eventP->eType == frmLoadEvent)       {       // 載入 Form 的資源號碼     formId = eventP->data.frmLoad.formID;     frmP = FrmInitForm(formId);     FrmSetActiveForm(frmP);     // 依據偵測到的 Form ID ,來決定設定目前 Form 的事件處理函式     switch (formId)       {      case MainForm:     // 將目前的 Form 事件處理函式,指向設為 MainFormHandleEvent( )      FrmSetEventHandler(frmP, MainFormHandleEvent);       break;       default:     break;       }       return true;       }       return false;     }     當 MainForm 被啟動時,會被 ApplicationHandleEvent( ) 抓到它的 FormID值,然後將本 Form 的事件處理函式設為 MainFormHandleEvent( ),也就是當 FrmDispatchEvent( ) 被執行時,會呼叫 MainFormHandleEvent( ) 函式,然後我們再將要處理的程式寫在 MainFormHandleEvent( ) 函式中.    再重覆一次,一個應用程式只需一個 ApplicationHandleEvent( ),然後看它需要幾個 Form,就有幾個 FormHandleEvent( ),也就是每個 Form 有它自己的事件處理函式,這是你應該注意的.    以下是 Palm OS公告的事件迴圈處理流程圖:     不過這是 Palm 不是bcb或delphi
dllee
站務副站長


發表:321
回覆:2519
積分:1711
註冊:2002-04-15

發送簡訊給我
#20 引用回覆 回覆 發表時間:2004-05-07 09:56:54 IP:220.139.xxx.xxx 未訂閱
感謝 conundrum 提供相關資訊  由 >沒空更新的網頁... href="http://dllee.adsldns.org">http://dllee.adsldns.org 介紹Shells,LiteStep,GeoShell....
------
http://www.ViewMove.com
hao_chih
一般會員


發表:15
回覆:25
積分:18
註冊:2003-09-10

發送簡訊給我
#21 引用回覆 回覆 發表時間:2004-05-07 12:41:23 IP:61.63.xxx.xxx 未訂閱
呃..插話一下討論一下(可能有點離題),> class="code"> timer_TimerEvent() { static int iCount; iCount++; while(1) { Sleep(100); if (bCheck) break; } } 這樣他在第一次接收到wm_timer時,主程序就會被鎖在裡頭(完全鎖住,沒有用到ProcessMessage),沒辦法出來,後面的wm_timer訊息就有可能被windows吃掉, 而有根本不會處理,iCount計數也不會正確。 所以dllee版大就在timer_event裡頭加上Application->PorcessMessage來防止wm_timer漏接,是吧? 在help裡頭針對application processmessage裡頭最重點的描述是 Interrupts the execution of an application so that Windows can process the message queue. 中斷目前的應用程式,處理windows的訊息佇列, 故函式的作用類似將目前的行程中斷,丟進堆疊,並將現在的訊息處理排在最後, 原本待在後面的訊息移到前面來處理,以防止後面的訊息過期而被系統給吃掉,我用Active Diagram畫了幾張圖,各位可以看一下 < src="http://frankmouse.myweb.hinet.net/1.png"> 以上還沒加入Application->ProcessMessage 抱歉~再下去用Active Diagram我就畫不出來了~可能要用 > (手邊的書都寫他是 > < src="http://frankmouse.myweb.hinet.net/Test1.jpg"> (主要利用自訂Event驅動,用TList追蹤) 目前實測開thread開到5000多沒問題,不過並不?,我測試的thread耗用的資源並不多。(P4 2.4,512MB RAM) 想不出什麼名堂可以將這個範例做個人發表 發表人 -
dllee
站務副站長


發表:321
回覆:2519
積分:1711
註冊:2002-04-15

發送簡訊給我
#22 引用回覆 回覆 發表時間:2004-05-07 14:23:10 IP:220.139.xxx.xxx 未訂閱
感謝 hao_chih 的分享,實在是太感動了 說真的, >,還有 > 對於 class="code"> procedure TTimer.UpdateTimer; begin KillTimer(FWindowHandle, 1); if (FInterval <> 0) and FEnabled and Assigned(FOnTimer) then if SetTimer(FWindowHandle, 1, FInterval, nil) = 0 then raise EOutOfResources.Create(SNoTimers); end; procedure TTimer.WndProc(var Msg: TMessage); begin with Msg do if Msg = WM_TIMER then try Timer; except Application.HandleException(Self); end else Result := DefWindowProc(FWindowHandle, Msg, wParam, lParam); end; 個人對 Timer 的認知是(可能有錯,歡迎指教): 以作業系統而言,Timer 不算是執行緒,只是在註冊後,系統固定時間發 WM_TIMER 給應用程式,如此,在工作管理員或是 IDE 的執行緒列表都不會看到有多出來的執行緒。而因為 OnTimer 事件也是在 WndProc 內被叫用,而此 WinProc 算是應用程式的主執行緒在執行,所以不需要 Synchronize 就已經是在主執行緒內執行了。 Application->ProcessMessages() 到目前為止的認知是(可能有錯,歡迎指教): 把執行權交出,由應用程式去處理其他的訊息(Message),當訊息都處理完後,執行權才會回來,接著由 Application->ProcessMessages() 之後的程式繼續執行。 原問題程式寫得不好,就會使兩個 Application->ProcessMessages() 互等不會結束... To hao_chih: 您的大作,可以命名為: 以多執行緒處理函式重入問題多執行緒範例程式自定 Event Queue 可模擬系統處理 Message Queue、可防重入 或其他您認為合適的名稱 說真的,您的程式簡潔有力,讓我見識到 >只要自己 > <>沒空更新的網頁... href="http://dllee.adsldns.org">http://dllee.adsldns.org 介紹Shells,LiteStep,GeoShell....
------
http://www.ViewMove.com
jest0024
高階會員


發表:11
回覆:310
積分:224
註冊:2002-11-24

發送簡訊給我
#23 引用回覆 回覆 發表時間:2004-05-07 15:15:38 IP:203.67.xxx.xxx 未訂閱
引言: Application->ProcessMessages() 到目前為止的認知是(可能有錯,歡迎指教): 把執行權交出,由應用程式去處理其他的訊息(Message),當訊息都處理完後,執行權才會回來,接著由 Application->ProcessMessages() 之後的程式繼續執行。 原問題程式寫得不好,就會使兩個 Application->ProcessMessages() 互等不會結束...
While(1){
  Application->ProcessMessages(); //有訊息時跳至處理的函數
                                  //無訊息時繼續往下執行
  //當然,程式依舊在主程序上執行..
}    轉貼ProcessMessages方法     procedure TApplication.ProcessMessages;
var
  Msg: TMsg;
begin
  while ProcessMessage(Msg) do {loop};
end;    function TApplication.ProcessMessage(var Msg: TMsg): Boolean;
var
  Handled: Boolean;
begin
  Result := False; //無訊息時傳回False
  if PeekMessage(Msg, 0, 0, 0, PM_REMOVE) then
  begin
    Result := True; //有訊息時傳回True
    if Msg.Message <> WM_QUIT then
    begin
      Handled := False;
      if Assigned(FOnMessage) then FOnMessage(Msg, Handled);
      if not IsHintMsg(Msg) and not Handled and not IsMDIMsg(Msg) and
        not IsKeyMsg(Msg) and not IsDlgMsg(Msg) then
      begin
        TranslateMessage(Msg);
        DispatchMessage(Msg);
      end;
    end
    else
      FTerminate := True;
  end;
end;
學而時習之不亦樂乎!
lu
高階會員


發表:11
回覆:189
積分:195
註冊:2003-11-19

發送簡訊給我
#24 引用回覆 回覆 發表時間:2004-05-07 16:08:18 IP:221.169.xxx.xxx 未訂閱
呵呵~~受教了各位,小弟在此拜謝   以往寫程式時都是知其然而不知所以然,書上都說 > 呵呵~~一直到開始轉用 >) 還好現在還是用 >) 呵呵~~版主大人,小弟建議,乾脆開個>大家一起快樂寫程式
dllee
站務副站長


發表:321
回覆:2519
積分:1711
註冊:2002-04-15

發送簡訊給我
#25 引用回覆 回覆 發表時間:2004-05-07 17:10:05 IP:220.139.xxx.xxx 未訂閱
to lu: 開專題? 您是提另外再開一個新的討論文章嗎? 還是要開一個討論區? 光用執行緒要開一個版,天使大人可能不會同意吧 < > 目前我最傷腦筋的是,分數要給誰? 可不可以把我的分數送出去呢 < > 沒空更新的網頁... http://dllee.ktop.com.tw C及指標教學,計算機概論,資訊管理導論... http://dllee.adsldns.org 介紹Shells,LiteStep,GeoShell....
------
http://www.ViewMove.com
lu
高階會員


發表:11
回覆:189
積分:195
註冊:2003-11-19

發送簡訊給我
#26 引用回覆 回覆 發表時間:2004-05-07 17:38:37 IP:221.169.xxx.xxx 未訂閱
引言: 開專題? 您是提另外再開一個新的討論文章嗎? 還是要開一個討論區? 光用執行緒要開一個版,天使大人可能不會同意吧 ㄟ....開篇新文章就好了吧....開一個討論區,就不用了吧 目前我最傷腦筋的是,分數要給誰? 可不可以把我的分數送出去呢 呵呵~~就此結案,都不給分如何? ... 偶會被打死,趕快~~酸 < face="Verdana, Arial, Helvetica"> ========================= 大家一起快樂寫程式
hao_chih
一般會員


發表:15
回覆:25
積分:18
註冊:2003-09-10

發送簡訊給我
#27 引用回覆 回覆 發表時間:2004-05-08 15:10:26 IP:61.229.xxx.xxx 未訂閱
呵呵~原來搞半天Timer是個"假"的執行緒啊< > 永遠在兜著自己的圈子~再怎麼兜也兜不出去< >.. 小弟最近在幫公司寫RS232的程式,看了一本書(C Builder與RS232串列通訊控制), 逮個正著,引用那本書的一段話.... 第3-78頁最上頭
引言: 使用執行緒的好處是可以另外產生一個執行的單位,不必排在應用程式的主執行緒中;我們也可以發現,這樣子的實驗結果和計時器似乎有異曲同工之處(實際上,計時器也是產生一個執行緒,只是我們看不出來而已)
這本書看到2/3,到目前為止,寫的很不錯哦,這只是個小"筆誤"吧~
系統時間:2024-06-26 10:46:04
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!