【发布时间】:2012-03-04 01:15:39
【问题描述】:
我正在开发一个多线程系统,该系统涉及线程通知其父对象销毁。问题是,我需要允许该对象保持创建足够长的时间,以便该事件读取该对象,因为我将该对象作为事件的参数之一传递。目前,当该事件被触发时,传递给该事件的对象已经从线程内销毁。
我不希望线程必须等待此事件完成,而是知道何时触发此事件并然后销毁该对象。我希望线程中的代码无论如何都继续,即使对象仍然被实例化。
这些对象有一个列表,它们是从一个线程中创建的。当此列表中的对象发生某些事情时(特别是在这种情况下对象的销毁),线程自身具有事件。我实际上是将这些事件输入事件队列(一个 TList,其中包含指向什么事件和什么对象的记录指针)。所以在线程的某个地方,我在这个事件列表中添加了一条记录。
然后线程的执行随之而来,并循环通过此列表中的事件并相应地触发它们(下面的示例)。因此,当事件被添加到列表中时,它会将对象指针保存为该事件记录指针的一部分。然后可能会有很长的延迟,直到事件实际被触发。此时,仍需要实例化对象,以便可以从线程外部读取它。只有这样才能真正销毁对象。
用于此事件队列的机制没有任何空间用于向线程反馈。它已经是一个已开发的系统,并且任何添加此事件队列以告诉线程该事件已被触发是不可能的,因为它需要整个重写。否则,一旦我的事件被调用,我会简单地告诉线程销毁这个对象。
这里有一些sn-ps,系统实际上非常大,所以很难展示所有的功能。一个线程的事件通过另外 4 个父对象引发一系列事件,并通过每个父对象传递该对象。目标是防止线程外的任何代码处理这种实际破坏。线程应该完全负责在销毁之前等待这个事件......
type
TJDNetSvrNode = class;
TJDNetSvrThread = class;
TNodeEvent = (neUnload); //And many more
PNodeEventRec = ^TNodeEventRec;
TNodeEventRec = record
Event: TNodeEvent;
Node: TJDNetSvrNode;
end;
TJDNetSvrNodeEvent = procedure(Sender: TObject; Node: TJDNetSvrNode) of object;
TJDNetSvrNode = class(TObject)
//Large object with no relevant members
end;
TJDNetSvrThread = class(TThread)
private
FNodeEvents: TList;
FNodeEvent: PNodeEventRec;
FOnNodeUnload: TJDNetSvrNodeEvent;
procedure SYNC_OnUnload;
public
property OnNodeUnload: TJDNetSvrNodeEvent read FOnNodeUnload write FOnNodeUnload;
end; //Much more in this class
//Starting point of event - adds to event queue list
procedure TJDNetSvrThread.NodeUnloaded(Sender: TObject; Node: TJDNetSvrNode);
var
E: PNodeEventRec;
begin
E:= New(PNodeEventRec);
E.Event:= neUnload;
E.Node:= Node;
FNodeEvents.Add(E);
end;
//Called within thread to execute any events which are queued
procedure TJDNetSvrThread.ProcessNodeEvents;
begin
while FNodeEvents.Count > 0 do begin
FNodeEvent:= PNodeEventRec(FNodeEvents[0]);
FNodeEvents.Delete(0);
case FNodeEvent.Event of
neUnload: begin
Synchronize(SYNC_OnUnload);
end;
//And many more
end;
Dispose(FNodeEvent);
end;
end;
procedure TJDNetSvrThread.SYNC_OnUnload;
begin
if assigned(FOnNodeUnload) then
FOnNodeUnload(Self, FNodeEvent.Node); //Parent also has to use "Node" for its event
//NOW "Node" can be destroyed
end;
【问题讨论】:
-
我不明白。使用 Synchronize() 调用“SYNC_OnUnload”操作例程,因此事件处理程序也被同步。为什么你不能在事件处理程序被调用后释放节点?如果“FOnNodeUnload”处理程序将节点存储或排队到另一个线程,那么释放节点的责任应该与它一起存储或排队。
-
如果事件处理程序需要选择是否“保留”对象,我通常将对象作为 var 参数传递到事件中,然后如果它仍然被分配则释放它。然后,事件处理程序可以选择通过存储/排队对象并将传递的引用设置为 nil 来保持对对象的责任。
-
@MartinJames 因为这些节点是从线程内的另一个对象(调用事件处理程序
TJDNetSvrThread.NodeUnloaded)中创建/管理/销毁的。对不起,我之前没有指出这一点。从技术上讲,即使是线程 (TJDNetSvrThread) 也不应该与告诉它销毁有任何关系,而是包含此节点对象列表的子对象 (TJDNetSvrNodes)。到目前为止,我能看到的唯一方法是在事件调用之后立即释放SYNC_OnUnload(正如你提到的),我真的不想从这里销毁它。 -
... 毫无疑问,这确实可以正常工作,只是从一个类中创建一个对象并从一个完全不知道的对象中销毁它真的很草率,而实际上该对象是在一个完全不同的单元中声明的。
-
'从一个类中创建一个对象并从一个完全不知道的对象中销毁它,实际上是在一个完全不同的单元中声明的,这真的很草率。' - 不幸的是,在线程之间进行通信时,这几乎是必须的。我知道的唯一选择是通过池回收对象。
标签: multithreading delphi events delphi-xe2