消息如何在多个 WndProc 之间序列化?
How the messages are serialized between multiple WndProcs?
我在主线程中创建了两个 WndProc,然后我 post 从其他线程向它们中的每一个发送消息,几乎同时,但从 WndProc1 开始。这个 WndProc1 有一项工作要做,它会持续一段时间……它会在开始和结束时发出信号。 WndProc2 也会在访问时发出信号。现在,当我按下按钮开始这个测试时,我得到:“P1-Enter ... [delay] ... P1-Leave WndProc2”。可以看到,第二条消息等待WndProc1完成,虽然他被发送到了WndProc2!我想知道的是如果这两个 WndProcs 没有任何共同点,这个序列化是如何工作的?我认为如果我有两个不同的组件,每个组件都有自己的 WndProc,甚至会发生这种情况(但我没有检查)。
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, SyncObjs;
type
TMyThread = class(TThread)
private
FHnd: HWND;
FTime: Integer;
protected
procedure Execute; override;
public
constructor Create(AHnd: HWND; ATime: Integer);
end;
TForm1 = class(TForm)
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
TestHand1, TestHand2: HWND;
MyT1, MyT2: TMyThread;
protected
procedure TestWndProc1(var Msg: TMessage);
procedure TestWndProc2(var Msg: TMessage);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
constructor TMyThread.Create(AHnd: HWND; ATime: Integer);
begin
inherited Create;
FHnd:= AHnd;
FTime:= ATime;
end;
procedure TMyThread.Execute;
begin
Sleep(FTime);
PostMessage(FHnd, WM_USER, 0, 0);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
TestHand1:= AllocateHWnd(TestWndProc1);
TestHand2:= AllocateHWnd(TestWndProc2);
end;
procedure TForm1.TestWndProc1(var Msg: TMessage);
var I: Integer;
A, B, C: Cardinal;
begin
if Msg.Msg = WM_USER then begin
Caption:= Caption + ' P1-Enter';
A:= 345678; B:= 765432;
for I:= 1 to 180000000 do begin
C:= A * B; B:= C * A; A:= B * C;
end;
Caption:= Caption + ' P1-Leave';
end;
end;
procedure TForm1.TestWndProc2(var Msg: TMessage);
begin
if Msg.Msg = WM_USER then
Caption:= Caption + ' WndProc2';
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Caption:= '';
MyT1:= TMyThread.Create(TestHand1, 300);
MyT2:= TMyThread.Create(TestHand2, 350);
end;
end.
所见即所得。每个线程都有一个且只有一个消息队列,并且可能有零到多个 window 句柄。 Window 句柄通常对应于可视组件,但不一定如您的示例所示。
代码中的某处(对于 Delphi 中的 GUI,这是在 Forms 单元中),有一个所谓的“消息循环”,用于检索消息从队列中分派到相应的 WndProc
。调度机制就像一个简单的函数调用:它在处理消息时阻塞,除非消息处理程序再次调用消息泵(其中 reentrancy problems start, if not handled correctly). Look at the documentation 并查看 Forms 单位 TApplication.ProcessMessages
和 类 单位 AllocateHWnd
/DeallocateHWnd
.
如果你想并行执行代码,你必须创建单独的线程;只要线程数少于 CPU 个核心并且线程未被 I/O 或互斥体、信号量和临界区阻塞,每个线程都将并行执行。如果有太多线程准备执行,它们将使用抢占式多任务处理进行调度。
您可以使用消息在线程之间进行通信。为此,线程必须创建一个 window 句柄并具有一个消息泵。
在Delphi中,GUI只能从主线程访问。如果辅助线程有东西要显示,那么它必须让显示代码由主线程执行,很可能再次通过 secondary/worker 线程和主线程之间的消息,或者使用 Synchronize
方法,或者使用其他通信机制,例如管道、套接字、文件 I/O、共享内存。单独或组合。
我在主线程中创建了两个 WndProc,然后我 post 从其他线程向它们中的每一个发送消息,几乎同时,但从 WndProc1 开始。这个 WndProc1 有一项工作要做,它会持续一段时间……它会在开始和结束时发出信号。 WndProc2 也会在访问时发出信号。现在,当我按下按钮开始这个测试时,我得到:“P1-Enter ... [delay] ... P1-Leave WndProc2”。可以看到,第二条消息等待WndProc1完成,虽然他被发送到了WndProc2!我想知道的是如果这两个 WndProcs 没有任何共同点,这个序列化是如何工作的?我认为如果我有两个不同的组件,每个组件都有自己的 WndProc,甚至会发生这种情况(但我没有检查)。
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, SyncObjs;
type
TMyThread = class(TThread)
private
FHnd: HWND;
FTime: Integer;
protected
procedure Execute; override;
public
constructor Create(AHnd: HWND; ATime: Integer);
end;
TForm1 = class(TForm)
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
TestHand1, TestHand2: HWND;
MyT1, MyT2: TMyThread;
protected
procedure TestWndProc1(var Msg: TMessage);
procedure TestWndProc2(var Msg: TMessage);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
constructor TMyThread.Create(AHnd: HWND; ATime: Integer);
begin
inherited Create;
FHnd:= AHnd;
FTime:= ATime;
end;
procedure TMyThread.Execute;
begin
Sleep(FTime);
PostMessage(FHnd, WM_USER, 0, 0);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
TestHand1:= AllocateHWnd(TestWndProc1);
TestHand2:= AllocateHWnd(TestWndProc2);
end;
procedure TForm1.TestWndProc1(var Msg: TMessage);
var I: Integer;
A, B, C: Cardinal;
begin
if Msg.Msg = WM_USER then begin
Caption:= Caption + ' P1-Enter';
A:= 345678; B:= 765432;
for I:= 1 to 180000000 do begin
C:= A * B; B:= C * A; A:= B * C;
end;
Caption:= Caption + ' P1-Leave';
end;
end;
procedure TForm1.TestWndProc2(var Msg: TMessage);
begin
if Msg.Msg = WM_USER then
Caption:= Caption + ' WndProc2';
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Caption:= '';
MyT1:= TMyThread.Create(TestHand1, 300);
MyT2:= TMyThread.Create(TestHand2, 350);
end;
end.
所见即所得。每个线程都有一个且只有一个消息队列,并且可能有零到多个 window 句柄。 Window 句柄通常对应于可视组件,但不一定如您的示例所示。
代码中的某处(对于 Delphi 中的 GUI,这是在 Forms 单元中),有一个所谓的“消息循环”,用于检索消息从队列中分派到相应的 WndProc
。调度机制就像一个简单的函数调用:它在处理消息时阻塞,除非消息处理程序再次调用消息泵(其中 reentrancy problems start, if not handled correctly). Look at the documentation 并查看 Forms 单位 TApplication.ProcessMessages
和 类 单位 AllocateHWnd
/DeallocateHWnd
.
如果你想并行执行代码,你必须创建单独的线程;只要线程数少于 CPU 个核心并且线程未被 I/O 或互斥体、信号量和临界区阻塞,每个线程都将并行执行。如果有太多线程准备执行,它们将使用抢占式多任务处理进行调度。
您可以使用消息在线程之间进行通信。为此,线程必须创建一个 window 句柄并具有一个消息泵。
在Delphi中,GUI只能从主线程访问。如果辅助线程有东西要显示,那么它必须让显示代码由主线程执行,很可能再次通过 secondary/worker 线程和主线程之间的消息,或者使用 Synchronize
方法,或者使用其他通信机制,例如管道、套接字、文件 I/O、共享内存。单独或组合。