Как сохранить объект потока созданным достаточно долго для события вне потока? - PullRequest
0 голосов
/ 04 марта 2012

Я работаю над многопоточной системой, которая включает поток, уведомляющий своего родителя о разрушении объекта. Проблема в том, что мне нужно позволить этому объекту оставаться созданным достаточно долго, чтобы это событие могло прочитать этот объект, потому что я передаю объект в качестве одного из параметров события. В настоящее время, когда это событие инициируется, объект, который передается в событие, уже уничтожен из потока.

Я не хочу, чтобы поток обязательно ждал завершения этого события, но скорее знаю, когда это событие было инициировано, и затем уничтожит объект. Я хочу, чтобы код в потоке продолжался, несмотря ни на что, даже если объект все еще создается.

Есть список этих объектов, и они созданы из потока. Сам поток имеет события, когда определенные вещи происходят с объектами в этом списке (особенно в этом случае уничтожение объекта). Я на самом деле кормить эти события в очередь событий (TList, который содержит указатели записи на какое событие и какой объект). Итак, где-то внутри темы я добавляю запись в этот список событий.

Затем выполняется выполнение потока, циклически перебирает события в этом списке и запускает их соответствующим образом (пример ниже). Поэтому, когда событие было добавлено в список, оно сохраняло указатель объекта как часть указателя записи этого события. Тогда может быть долгая задержка, пока событие фактически не сработает. На этом этапе объект еще должен быть создан, чтобы его можно было прочитать из потока. Только тогда объект действительно будет уничтожен.

Механизм, используемый для этой очереди событий, не имеет места для обратной связи с потоком. Это уже разработанная система, и любое добавление для этой очереди событий в говорит потоку , что событие было инициировано, не может быть и речи, поскольку для этого потребуется полная перезапись. В противном случае я бы просто сказал потоку уничтожить этот объект после вызова моего события.

Вот некоторые фрагменты, система на самом деле очень большая, поэтому сложно показать всю функциональность. Событие одного потока запускает цепочку событий через еще 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;

1 Ответ

5 голосов
/ 04 марта 2012

Похоже, что вы в основном хотите, чтобы объект имел возможность ссылаться из нескольких разных мест в системе и не был уничтожен до тех пор, пока все места, на которые он ссылается, не были сделаны с ним, независимо от того, в каком порядке это происходит в .

В Delphi есть механизм, который делает именно это: подсчет ссылок. Он встроен в модель Interface. Попробуйте превратить ваш объект в TInterfacedObject потомка, создайте и внедрите Interface, который предоставляет необходимую вам функциональность, и передайте его вместо ссылки на объект.

...