Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Multithreading Delphi-跨线程事件处理_Multithreading_Delphi_Delphi 6 - Fatal编程技术网

Multithreading Delphi-跨线程事件处理

Multithreading Delphi-跨线程事件处理,multithreading,delphi,delphi-6,Multithreading,Delphi,Delphi 6,我有一个小型的客户机-服务器应用程序,其中服务器使用命名管道向客户机发送一些消息。客户机有两个线程—主GUI线程和一个“接收线程”,它不断接收服务器通过命名管道发送的消息。现在,每当收到某条消息时,我都想触发一个自定义事件——但是,该事件不应该在调用线程上处理,而应该在主GUI线程上处理——我不知道该怎么做(甚至不知道是否可能) 以下是我目前掌握的情况: tMyMessage = record mode: byte; //...some other fields... end;

我有一个小型的客户机-服务器应用程序,其中服务器使用命名管道向客户机发送一些消息。客户机有两个线程—主GUI线程和一个“接收线程”,它不断接收服务器通过命名管道发送的消息。现在,每当收到某条消息时,我都想触发一个自定义事件——但是,该事件不应该在调用线程上处理,而应该在主GUI线程上处理——我不知道该怎么做(甚至不知道是否可能)

以下是我目前掌握的情况:

tMyMessage = record
    mode: byte;
    //...some other fields...
end;

TMsgRcvdEvent = procedure(Sender: TObject; Msg: tMyMessage) of object;

TReceivingThread = class(TThread)
private
  FOnMsgRcvd: TMsgRcvdEvent;
  //...some other members, not important here...
protected
  procedure MsgRcvd(Msg: tMyMessage); dynamic;
  procedure Execute; override;
public
  property OnMsgRcvd: TMsgRcvdEvent read FOnMsgRcvd write FOnMsgRcvd;
  //...some other methods, not important here...
end;

procedure TReceivingThread.MsgRcvd(Msg: tMyMessage);
begin
  if Assigned(FOnMsgRcvd) then FOnMsgRcvd(self, Msg);
end;

procedure TReceivingThread.Execute;
var Msg: tMyMessage
begin
  //.....
  while not Terminated do begin //main thread loop
    //.....
    if (msgReceived) then begin
      //message was received and now is contained in Msg variable
      //fire OnMsgRcvdEvent and pass it the received message as parameter
      MsgRcvd(Msg); 
    end;
    //.....
  end; //end main thread loop
  //.....
end;
例如,现在我希望能够创建事件处理程序作为TForm1类的成员

procedure TForm1.MessageReceived(Sender: TObject; Msg: tMyMessage);
begin
  //some code
end;
这不会在接收线程中执行,而是在主UI线程中执行。我特别希望接收线程只触发事件并继续执行,而不等待事件处理程序方法的返回(基本上我需要类似.NET方法的东西)


我真的是一个初学者(几小时前我就试着学习如何定义自定义事件),所以我甚至不知道这是否可能,或者我是否做了错事,所以提前感谢您的帮助。

检查文档以了解同步方法。它是为像您这样的任务设计的。

您应该使用PostMessage(异步)或SendMessage(同步)API向“窗口”发送消息。您还可以使用某种“队列”或使用奇妙的OmniThreadLibrary来“执行此操作(高度推荐)

声明私有成员”

FRecievedMessage: TMyMEssage
和受保护的程序

procedure PostRecievedMessage;
begin
   if Assigned(FOnMsgRcvd) then FOnMsgRcvd(self, FRecievedMessage);
   FRecievedMessage := nil;
end;
并将循环中的代码更改为

if (msgReceived) then begin
  //message was received and now is contained in Msg variable
  //fire OnMsgRcvdEvent and pass it the received message as parameter
  FRecievedMessage := Msg;
  Synchronize(PostRecievedMessage); 
end;

如果您想完全异步,请使用PostMessage API。

您已经有了一些答案,但没有一个提到问题中令人不安的部分:

tMyMessage = record
    mode: byte;
    //...some other fields...
end;
请注意,在.NET环境中,当您使用Delphi或其他包装器进行本机Windows消息处理时,您无法完成所有您认为理所当然的事情。您可能希望能够将随机数据结构传递给事件处理程序,但这是行不通的。原因是需要内存管理

在.NET中,您可以确保不再从任何地方引用的数据结构将被垃圾收集处理掉。在Delphi中,您没有同样的余地,您需要确保任何分配的内存块也被正确释放

在Windows中,消息接收器是一个窗口句柄(一个
HWND
),可以
SendMessage()
PostMessage()
发送给它,或者是一个线程,可以
PostThreadMessage()
发送给它。在这两种情况下,消息只能携带两个数据成员,它们都是机器字宽度,第一个是类型
WPARAM
,第二个是类型
LPARAM
)。您不能简单地将任何随机记录作为消息参数发送或发布

Delphi使用的所有消息记录类型都具有基本相同的结构,这映射到上面的数据大小限制

如果您想将数据发送到另一个包含两个以上32位大小变量的线程,那么事情就会变得棘手。由于可以发送的值的大小限制,您可能无法发送整个记录,而只能发送其地址。要做到这一点,您需要在发送线程中动态分配数据结构,将地址作为消息参数之一传递,并将接收线程中的相同参数重新解释为具有相同类型的变量的地址,然后使用记录中的数据,并释放动态分配的内存结构

因此,根据需要发送到事件处理程序的数据量,您可能需要更改
tMyMessage
记录。这是可以实现的,但它比必要的更困难,因为类型检查不适用于事件数据

我建议以不同的方式处理这个问题。您知道需要将哪些数据从工作线程传递到GUI线程。只需创建一个排队数据结构,将事件参数数据放入其中,而不是直接将其与消息一起发送。使此队列线程安全,即使用关键部分对其进行保护,以便即使从不同线程同时尝试添加或从队列中删除,也可以安全地进行添加或删除


要请求新的事件处理,只需将数据添加到队列中。仅当第一个数据元素添加到以前的空队列时,才向接收线程发布消息。然后,接收线程应该接收并处理消息,并继续从队列中弹出数据元素并调用匹配的事件处理程序,直到队列再次为空。为获得最佳性能,应尽快锁定队列,并且在调用事件处理程序时,一定要暂时再次解锁队列。

如果您希望签出队列(),我的框架可以为您执行此操作。

。Synchronize停止所有次线程并在主线程的上下文中执行,这意味着它首先破坏了拥有多个线程的目的。有很多比同步更好的方法。不过,我不会投你反对票,因为尽管这是一个糟糕的答案,但从技术上讲,这是一个有效的答案。:-)谁告诉你那个神话的?同步不会阻止“所有”次线程。它只会阻塞调用它的线程,这是由于它的同步性质。但是其他线程仍在运行。是的,只有调用线程被阻塞。这不是最好的机制,但如果您知道它是如何工作的,您应该可以。使用
Synchronize()
意味着确保事件处理程序在调用返回时已完成,即忽略问题的以下部分:“我特别希望接收线程只触发事件并继续执行,而不等待事件处理程序方法的返回”。它会阻止任何希望调用
Synchronize
的其他线程。+1因为不使用Synchronize。使用post/send message的示例将非常有用,特别是