title: Multi access array/list in thread-safe mode?
body: <p>has for some time that I'm trying to make sure this class with threads.
  Unfortunately I can not understand where to act properly.</p>
  
  <p>The problem:</p>
  
  <p>The saving procedures take a long time, therefore I would like to use it in a dedicated thread. 
  The idea is that every change in the root node, is increased the demand for saving if the thread saving is running. When the thread of saving ends if there are requests then resave still zeroing requests until there aren't requests.</p>
  
  <p>But until I add nodes, there is no problem. At the moment, however, to remove a node (then all child nodes), if the thread saving is running, then creates rightly exceptions.</p>
  
  <p>There is a solution or another way to save the root node at the same time making it editable. 
  Obviously without duplicating the entire node for each saving request?</p>
  
  <p>Thank you all.</p>
  
  <p>Base Class:</p>
  
  <pre><code>interface
  
  uses
  
    System.Classes, System.SysUtils;
  
  type
  
    TNode&lt;T&gt; = class
    public type
      TNodeForEachProc = reference to procedure( const ANode: TNode&lt;T&gt;; var ABreak: Boolean );
      TNodeWriteProc = reference to procedure( const AWriter: TBinaryWriter; const AData: T );
      TNodeReadProc = reference to function( const AReader: TBinaryReader ): T;
      TNodeBufferedWriteProc = reference to procedure( const AWriter: TBufferedWriter; const AData: T );
      TNodeBufferedReadProc = reference to function( const AReader: TBufferedReader ): T;
      TNodeProgressProc = reference to procedure( const AIndex, ACount: Integer );
    private
      FCount: Integer;
      FCapacity: Integer;
      FNodes: TArray&lt;TNode&lt;T&gt;&gt;;
      {$IFDEF AUTOREFCOUNT}[Weak]{$ENDIF}FParent: TNode&lt;T&gt;;
      procedure Grow;
      procedure SetCapacity( const AValue: Integer );
      function Get( AIndex: Integer ): TNode&lt;T&gt;; inline;
      function GetIndex: Integer;
      function GetLevel: Integer;
      procedure Adopt( const ANode: TNode&lt;T&gt; );
      procedure Orphan;
      procedure SetParent( const AValue: TNode&lt;T&gt; );
    protected
      property Nodes:TArray&lt;TNode&lt;T&gt;&gt; read FNodes;
    public
      Data: T;
      constructor Create( const ACapacity: Integer = 0 ); overload;
      constructor Create( const ANode: TNode&lt;T&gt; ); overload;
      destructor Destroy; override;
      procedure Assign( const ANode: TNode&lt;T&gt; );
      function Add( const AData: T ): TNode&lt;T&gt;;
      procedure Clear; inline;
      function Count( const ARecursive: Boolean = False ): Integer; inline;
      function Empty: Boolean; inline;
      procedure Delete( const AIndex: Integer; const ACount: Integer = 1 );
      function ForEach( const AProc: TNodeForEachProc; const ARecursive: Boolean = True ): Boolean;
      property Index: Integer read GetIndex;
      property Node[ Index: Integer ]: TNode&lt;T&gt; read Get; default;
      property Level: Integer read GetLevel;
      property Parent: TNode&lt;T&gt; read FParent write SetParent;
      procedure SaveToStream( const AStream: TStream; const AWriterProc: TNodeWriteProc; const AProgressProc: TNodeProgressProc = nil );
      procedure LoadFromStream( const AStream: TStream; const AReaderProc: TNodeReadProc; const AProgressProc: TNodeProgressProc = nil );
      procedure SaveToFile( const AFileName: TFileName; const AWriterProc: TNodeBufferedWriteProc; const AProgressProc: TNodeProgressProc = nil );
      procedure LoadFromFile( const AFileName: TFileName; const AReaderProc: TNodeBufferedReadProc; const AProgressProc: TNodeProgressProc = nil );
    end;
  
  implementation
  
  { TNode&lt;T&gt; }
  
  constructor TNode&lt;T&gt;.Create( const ACapacity: Integer = 0 );
  begin
    SetCapacity( ACapacity );
  end;
  
  constructor TNode&lt;T&gt;.Create( const ANode: TNode&lt;T&gt; );
  begin
    Assign( ANode );
  end;
  
  destructor TNode&lt;T&gt;.Destroy;
  begin
    Clear;
    Orphan;
    inherited;
  end;
  
  procedure TNode&lt;T&gt;.Assign( const ANode: TNode&lt;T&gt; );
  var
    VIndex: Integer;
  begin
    Clear;
    Data := ANode.Data;
    FCount := ANode.FCount;
    SetLength( FNodes, FCount );
    for VIndex := 0 to FCount - 1 do
    begin
      FNodes[ VIndex ] := TNode&lt;T&gt;.Create;
      FNodes[ VIndex ].Assign( ANode.FNodes[ VIndex ] );
    end;
  end;
  
  function TNode&lt;T&gt;.Add( const AData: T ): TNode&lt;T&gt;;
  begin
    Result := TNode&lt;T&gt;.Create;
    Result.Data := AData;
    Adopt( Result );
  end;
  
  procedure TNode&lt;T&gt;.Clear;
  begin
    Delete( 0, FCount );
    FNodes := nil;
    FCount := 0;
    FCapacity := 0;
  end;
  
  function TNode&lt;T&gt;.Count( const ARecursive: Boolean = False ): Integer;
  var
    VCount: Integer;
  begin
    if ARecursive then
    begin
      VCount := 0;
      ForEach(
        procedure( const Node: TNode&lt;T&gt;; var ABreak: Boolean )
        begin
          Inc( VCount );
        end
      );
      Result := VCount;
    end else
      Result := FCount;
  end;
  
  procedure TNode&lt;T&gt;.Delete( const AIndex: Integer; const ACount: Integer = 1 );
  var
    VIndex: Integer;
  begin
    for VIndex := AIndex to AIndex + ACount - 1 do
    begin
      FNodes[ VIndex ].FParent := nil;
      FNodes[ VIndex ].DisposeOf;
      Dec( FCount );
    end;
    System.Delete( FNodes, AIndex, ACount );
  end;
  
  function TNode&lt;T&gt;.Empty: Boolean;
  begin
    Result := FCount = 0;
  end;
  
  function TNode&lt;T&gt;.ForEach( const AProc: TNodeForEachProc; const ARecursive: Boolean = True ): Boolean;
  type
    TTypeNode = TNode&lt;T&gt;;
  var
    VIndex: Integer;
    VTypeNode: TTypeNode;
    VBreak: Boolean;
  begin
    Result := True;
    if Assigned( AProc ) then
      for VIndex := 0 to Count - 1 do
      begin
        VTypeNode := FNodes[ VIndex ];
        VBreak := False;
        AProc( VTypeNode, VBreak );
        if VBreak then
          Exit( False );
        if ARecursive then
          if not VTypeNode.ForEach( AProc ) then
            Exit( False );
      end;
  end;
  
  procedure TNode&lt;T&gt;.Grow;
  var
    VDelta: Integer;
  begin
    if FCapacity &gt; 64 then
      VDelta := FCapacity div 4
    else
      if FCapacity &gt; 8 then
        VDelta := 16
      else
        VDelta := 4;
    SetCapacity( FCapacity + VDelta );
  end;
  
  procedure TNode&lt;T&gt;.SetCapacity( const AValue: Integer );
  begin
    if AValue &gt; FCapacity then
    begin
      SetLength( FNodes, AValue );
      FCapacity := AValue;
    end;
  end;
  
  function TNode&lt;T&gt;.Get( AIndex: Integer ): TNode&lt;T&gt;;
  begin
    Result := FNodes[ AIndex ];
  end;
  
  function TNode&lt;T&gt;.GetIndex: Integer;
  var
    VIndex: Integer;
  begin
    if FParent &lt;&gt; nil then
      for VIndex := 0 to FParent.Count - 1 do
        if FParent[ VIndex ] = Self then
          Exit( VIndex );
    Result := - 1;
  end;
  
  function TNode&lt;T&gt;.GetLevel: Integer;
  begin
    if FParent = nil then
      Result := 0
    else
      Result := FParent.Level + 1;
  end;
  
  procedure TNode&lt;T&gt;.Adopt( const ANode: TNode&lt;T&gt; );
  var
    VIndex: Integer;
  begin
    ANode.FParent := Self;
    VIndex := FCount;
    if VIndex = FCapacity then
      Grow;
    FNodes[ VIndex ] := ANode;
    Inc( FCount );
  end;
  
  procedure TNode&lt;T&gt;.Orphan;
  begin
    if FParent &lt;&gt; nil then
    begin
      System.Delete( FParent.FNodes, Index, 1 );
      Dec( FParent.FCount );
    end;
  end;
  
  procedure TNode&lt;T&gt;.SetParent( const AValue: TNode&lt;T&gt; );
  begin
    if FParent &lt;&gt; AValue then
    begin
      Orphan;
      FParent := AValue;
      if FParent &lt;&gt; nil then
        FParent.Adopt( Self );
    end;
  end;
  
  procedure TNode&lt;T&gt;.SaveToStream( const AStream: TStream; const AWriterProc: TNodeWriteProc; const AProgressProc: TNodeProgressProc = nil );
  var
    VCount: Integer;
    VIndex: Integer;
    VWriter: TBinaryWriter;
  begin
    if Assigned( AWriterProc ) then
    begin
      VCount := Count( True );
      VIndex := 0;
      VWriter := TBinaryWriter.Create( AStream );
      try
        VWriter.Write( VCount );
        AWriterProc( VWriter, Data );
        VWriter.Write( Count );
        ForEach(
          procedure( const ANode: TNode&lt;T&gt;; var ABreak: Boolean )
          begin
            AWriterProc( VWriter, ANode.Data );
            VWriter.Write( ANode.Count );
            if Assigned( AProgressProc ) then
            begin
              AProgressProc( VIndex, VCount );
              Inc( VIndex );
            end;
          end
        );
      finally
        VWriter.DisposeOf;
      end;
    end;
  end;
  
  procedure TNode&lt;T&gt;.LoadFromStream( const AStream: TStream; const AReaderProc: TNodeReadProc; const AProgressProc: TNodeProgressProc = nil );
  var
    VIndex: Integer;
    VCount: Integer;
    VReader: TBinaryReader;
    VCurrentNode: TNode&lt;T&gt;;
    VEntryNode: TNode&lt;T&gt;;
  begin
    if Assigned( AReaderProc ) then
    begin
      Clear;
      VReader := TBinaryReader.Create( AStream );
      try
        VCount := VReader.ReadInteger;
        Data := AReaderProc( VReader );
        SetCapacity( VReader.ReadInteger );
        VCurrentNode := Self;
        for VIndex := 0 to VCount - 1 do
        begin
          VEntryNode := VCurrentNode.Add( AReaderProc( VReader ) );
          VEntryNode.SetCapacity( VReader.ReadInteger );
          if VEntryNode.FCapacity &gt; 0 then
            VCurrentNode := VEntryNode
          else if VCurrentNode.FCount = VCurrentNode.FCapacity then
            if VCurrentNode.Parent &lt;&gt; nil then
            begin
              VCurrentNode := VCurrentNode.Parent;
              while ( VCurrentNode.FCount = VCurrentNode.FCapacity ) and ( VCurrentNode &lt;&gt; Self ) do
                if VCurrentNode.Parent = nil then
                  VCurrentNode := Self
                else
                  VCurrentNode := VCurrentNode.Parent;
            end else
              VCurrentNode := Self;
          if Assigned( AProgressProc ) then
            AProgressProc( VIndex, VCount );
        end;
      finally
        VReader.DisposeOf;
      end;
    end;
  end;
  
  procedure TNode&lt;T&gt;.SaveToFile( const AFileName: TFileName; const AWriterProc: TNodeBufferedWriteProc; const AProgressProc: TNodeProgressProc = nil );
  var
    VCount: Integer;
    VIndex: Integer;
    VWriter: TBufferedWriter;
  begin
    if Assigned( AWriterProc ) then
    begin
      VCount := Count( True );
      VIndex := 0;
      VWriter := TBufferedWriter.Create( AFileName );
      try
        VWriter.WriteInteger( VCount );
        AWriterProc( VWriter, Data );
        VWriter.WriteInteger( Count );
        if Assigned( AProgressProc ) then
        begin
          AProgressProc( VIndex, VCount );
          Inc( VIndex );
        end;
        ForEach(
          procedure( const ANode: TNode&lt;T&gt;; var ABreak: Boolean )
          begin
            AWriterProc( VWriter, ANode.Data );
            VWriter.WriteInteger( ANode.Count );
            if Assigned( AProgressProc ) then
            begin
              AProgressProc( VIndex, VCount );
              Inc( VIndex );
            end;
          end
        );
      finally
        VWriter.DisposeOf;
      end;
    end;
  end;
  
  procedure TNode&lt;T&gt;.LoadFromFile( const AFileName: TFileName; const AReaderProc: TNodeBufferedReadProc; const AProgressProc: TNodeProgressProc = nil );
  var
    VIndex: Integer;
    VCount: Integer;
    VReader: TBufferedReader;
    VCurrentNode: TNode&lt;T&gt;;
    VEntryNode: TNode&lt;T&gt;;
  begin
    if Assigned( AReaderProc ) then
    begin
      Clear;
      VReader := TBufferedReader.Create( AFileName );
      try
        VCount := VReader.ReadInteger;
        Data := AReaderProc( VReader );
        SetCapacity( VReader.ReadInteger );
        VCurrentNode := Self;
        for VIndex := 0 to VCount - 1 do
        begin
          VEntryNode := VCurrentNode.Add( AReaderProc( VReader ) );
          VEntryNode.SetCapacity( VReader.ReadInteger );
          if VEntryNode.FCapacity &gt; 0 then
            VCurrentNode := VEntryNode
          else if VCurrentNode.FCount = VCurrentNode.FCapacity then
            if VCurrentNode.Parent &lt;&gt; nil then
            begin
              VCurrentNode := VCurrentNode.Parent;
              while ( VCurrentNode.FCount = VCurrentNode.FCapacity ) and ( VCurrentNode &lt;&gt; Self ) do
                if VCurrentNode.Parent = nil then
                  VCurrentNode := Self
                else
                  VCurrentNode := VCurrentNode.Parent;
            end else
              VCurrentNode := Self;
          if Assigned( AProgressProc ) then
            AProgressProc( VIndex, VCount );
        end;
      finally
        VReader.DisposeOf;
      end;
    end;
  end;
  
  end.
  </code></pre>