9

TThread クラスのメソッドを理解して知っている限り、コードを同期すると、実際にはメインのアプリケーション スレッドで実行されます (タイマー/ボタンクリックなどのように)。 MessageBox はメイン アプリケーションをブロックしませんが、スリープは期待どおりにブロックします。何故ですか?

type
  TTestThread = class(TThread)
  private
    procedure SynchThread;
  protected
    procedure Execute; override;
  public
    constructor Create(CreateSuspended: Boolean);
  end;

procedure TTestThread.SynchThread;
begin
 MessageBoxA (0, 'Hello', 'Test', 0);
end;

procedure TTestThread.Execute;
begin
 Synchronize (SynchThread)
end;

constructor TTestThread.Create(CreateSuspended: Boolean);
begin
  inherited;
  FreeOnTerminate := True;
end;

procedure StartThread;
var
 TestThread : TTestThread;
begin
 TestThread := TTestThread.Create (FALSE);
end;
4

3 に答える 3

13

この答えには 2 つの部分があります。

パート 1 は、If MessageBox()/related が同期している場合、メッセージ ループがフリーズしないのはなぜですか?でうまく説明されています。. MessageBox 関数はブロッキングではなく、独自のメッセージ ループを持つダイアログ ボックスを作成するだけです。

パート 2 は、MessageBox のドキュメントで説明されています。

hWnd: 作成するメッセージ ボックスのオーナー ウィンドウへのハンドル。このパラメーターが NULL の場合、メッセージ ボックスにはオーナー ウィンドウがありません。

モーダル ダイアログを表示すると、Windows はその所有者を無効にしますが、最初のパラメーターに 0 を渡すと、所有者はなく、無効にするものは何もありません。したがって、メッセージ ボックスが表示されている間、プログラムはメッセージの処理 (および応答) を続けます。

この動作を変更するには、フォームのハンドルを最初のパラメーターとして渡します。例えば:

procedure TTestThread.SynchThread;
begin
  MessageBoxA (Form1.Handle, 'Hello', 'Test', 0);
end;
于 2013-03-29T07:31:05.117 に答える
12

質問は、あなたが次のように言うときの意味に要約されると思います。

メッセージ ボックスは、メイン アプリケーションをブロックしません。

これは、メッセージ ボックスを表示しても VCL フォームを操作できるということです。ここでの問題はスレッドとは無関係であり、方程式からスレッドを削除することをお勧めします。何をするかについてのあなたの理解Synchronizeは正しいです。

この問題は、ウィンドウの所有者の概念と、所有者に関してモーダル ダイアログ ウィンドウがどのように動作するかに完全に関連しています。所有者とは、Delphi プロパティを意味するのではなく、所有者TComponent.Ownerの Windows API の意味を意味することに注意してください。

VCL アプリを作成し、フォームに 2 つのボタンをドロップします。OnClick次のハンドラーを追加します。

procedure TForm1.Button1Click(Sender: TObject);
begin
  MessageBox(0, 'Not owned', nil, MB_OK);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  MessageBox(Handle, 'Owned by the VCL form', nil, MB_OK);
end;

をクリックすると何が起こるかを観察してくださいButton1。メッセージ ボックスが表示されますが、VCL フォームは引き続きクリックできます。と比較してくださいButton2。メッセージ ボックスが表示されると、VCL フォームを操作できません。

モーダル ダイアログ ウィンドウが表示されると、ダイアログ ウィンドウはその所有者を無効にします。の場合Button2、所有者は VCL フォームです。また、フォームが無効になると、操作できなくなります。の場合、Button1所有者がいないため、モーダル ダイアログ ウィンドウは他のウィンドウを無効にしません。そのため、VCL フォームを操作できます。

Raymond Chen は、彼の Old New Thing ブログでモダリティに関する長いシリーズを公開しています。

于 2013-03-29T09:00:08.083 に答える
4

Synchronize はメインスレッドでコードを実行します。
良い説明はここにありますDelphi TThreadクラスの同期

ユーザーがアプリケーションのフォームとやり取りできないようにする必要があります。に

procedure TTestThread.SynchThread;
begin
MessageBoxA (0, 'Hello', 'Test', MB_TASKMODAL);      
end;

あなたが行ったように MessageBoxA を使用しても、ユーザーがフォームと対話することによってトリガーされたイベントにメインスレッドが反応するのを妨げることはありません。試してみてください。

procedure TForm4.Button2Click(Sender: TObject);
begin
    MessageBoxA (0, 'Hello', 'Test', 0);
   // vs
   //  MessageBoxA (0, 'Hello', 'Test', MB_TASKMODAL);
end;

メッセージボックスA

メインスレッドで同期が実行されることは、(IMHO)によって表示できます

type
  TTestThread = class(TThread)
  private
    FSync:Boolean;
    FCalled:TDateTime;
    procedure SynchThread;
  protected
    procedure Execute; override;
  public
    constructor Create(CreateSuspended: Boolean;sync:Boolean);
  end;

procedure TTestThread.SynchThread;
begin
 MessageBox (0,PChar(DateTimeToStr(FCalled)+#13#10+DateTimeToStr(Now)),'Hello' , 0);
end;

procedure TTestThread.Execute;
begin
 sleep(100); // give Caller Time to fell asleep
 if Fsync then Synchronize (SynchThread) else SynchThread;
end;

constructor TTestThread.Create(CreateSuspended: Boolean;sync:Boolean);
begin
  inherited Create(CreateSuspended);
  FSync := Sync;
  FCalled :=Now;
  FreeOnTerminate := True;
end;

procedure StartThread(sync:Boolean);
var
 TestThread : TTestThread;
begin
 TestThread := TTestThread.Create (FALSE,sync);
end;

procedure TForm4.RunUnsynchronizedClick(Sender: TObject);
begin
   StartThread(false);// no sync
   Sleep(5000);       // Stop messageloop
end;

procedure TForm4.RunSynchronizedClick(Sender: TObject);
begin
   StartThread(true); // sync
   Sleep(5000);       // Stop messageloop
end;
于 2013-03-29T07:03:38.283 に答える