【问题标题】:How the messages are serialized between multiple WndProcs?消息如何在多个 WndProcs 之间序列化?
【发布时间】:2021-06-18 00:17:03
【问题描述】:

我在主线程中创建了两个 WndProc,然后我几乎同时从其他线程向每个线程发布消息,但从 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:= $12345678; B:= $98765432;
  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.

【问题讨论】:

  • 确实消息是从消息队列中一一取回的。在处理您之前发布的消息之前,您稍后发布的消息将不会被检索和处理。如您所见,不同的窗口过程或不同的组件,它们的共同点是消息队列——它们存在于同一个线程中,因此共享同一个队列。
  • 但这就是我不明白的......两个窗口过程(或更多)怎么会有相同的消息队列?
  • 消息队列是线程而不是窗口的特性。见documentation
  • 不确定是否明显,您创建了不同的线程,但这些不同的线程发布到同一个(主/gui)线程(因为它们发布到的窗口是由主线程创建的)。跨度>

标签: multithreading delphi message


【解决方案1】:

您所看到的完全符合预期。每个线程都有一个且只有一个消息队列,并且可能有零到多个窗口句柄。窗口句柄通常对应于可视组件,但不一定如您的示例中那样。

在代码的某个地方(对于 Delphi 中的 GUI,这是在 Forms 单元中),有一个所谓的“消息循环”,它从队列中检索消息并将它们分派到相应的WndProc。调度机制就像一个简单的函数调用:在处理消息时它是阻塞的,除非消息处理程序再次调用消息泵(reentrancy 问题开始,如果处理不正确)。查看documentation 并在Forms 单元中查看TApplication.ProcessMessages,在Classes 单元中查看AllocateHWnd/DeallocateHWnd

如果你想并行执行代码,你必须创建单独的线程;只要线程数少于 CPU 内核并且线程没有被 I/O 或互斥体、信号量和临界区阻塞,每个线程都将并行执行。如果准备好执行的线程过多,则会使用抢占式多任务来调度它们。

您可以使用消息在线程之间进行通信。为此,线程必须创建一个窗口句柄并拥有一个消息泵。

在 Delphi 中,只能从主线程访问 GUI。如果辅助线程有东西要显示,那么它必须让显示代码由主线程执行,很可能再次通过辅助/工作线程和主线程之间的消息,或者使用Synchronize方法,或者使用其他通信机制例如管道、套接字、文件 I/O、共享内存。单独或组合。

【讨论】:

  • 我不希望代码并行执行,事实上这正是我想要避免的。 :) 而且我想确保将消息发布到同一线程的多个窗口是线程安全的。谢谢你的好解释!如果您允许我再问一个问题,当我使用AllocateHwnd 创建一个新窗口时,我如何知道创建了哪个线程?
  • 请打开一个新问题,而不是在评论中提问。 AllocateHwnd 在调用线程的上下文中工作。
猜你喜欢
  • 2012-02-09
  • 2010-11-10
  • 1970-01-01
  • 1970-01-01
  • 2011-02-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-11
相关资源
最近更新 更多