Hi
I want to populate a tree (ie add nodes) from 2 background threads while at the same time allowing the user to interact with the tree (move nodes in design mode, open and close nodes etc). D5. TeeTree 2.0 (is there a later??)
The background threads call synchronize and I can set it up so that they each call a separate tree updating procedure or I guess I could use a critical section in the main thread so that neither worker (background) thread can update the tree until the other is finished.
Maybe that is sufficient for the issues relating to the tree nodes (GUI issues below), but maybe I would be better off replacing TteeList with TthreadedList (keeping your speed enhancements of course <g>).
OK, can you give me your advice on this aspect?
Now, what about the gui and the canvas and any tree-modifying events that the user might generate through mouse and keyboard clicks (eg delete a node) and simple display-modifying events (like scroll). ??
It seems to me that it would be nice to lock the canvas when worker thread tree-updating events are happening, but I am unsure how to do this
(and I am not sure it would be sufficinet), and I would welcome some advice.
Also, I am unsure of the relationship between the various canvasses (canvi?) and some explanation would be appreciated.
I see that
Teecanvas is just class(Tobject) but contains a
property ReferenceCanvas : Tcanvas
but TcustomTeePanel has a DelphiCanvas.
Also, in your CustomShape.pas you draw direct to Tree.Canvas ..
so the tree must have a canvas (but is that a Tcanvas?)
hmm. How do all these fit together? And what do I lock?
I see at
http://www.teechart.net/support/modules ... icle&sid=6
which is the article on realtime charting you suggest directly fiddling around with critical sections for performance reasons
// When using only a single thread, disable locking:
Chart1.Canvas.ReferenceCanvas.Pen.OwnerCriticalSection := nil;
Series1.LinePen.OwnerCriticalSection := nil;
This seems to suggest that there are already critical sections in place but maybe these are just for low level objects like Pen and Brush.
Thanks, in advance, for your help.
Thread safety in TeeTree
-
- Newbie
- Posts: 20
- Joined: Sat Oct 04, 2003 4:00 am
Populating a tree using a thread
Is there anyone listening to this ? Tom?
Any ideas?
Any ideas?
Hi,
Sorry, I'm not sure if I can provide you valuable information concerning your issues. I've forwarded your question to others who might give more information.
The latest version of TeeTree is available throught the TeeChart PRO package (starting from v7). This is still TeeTree 2.0
Isn't the synchronize method sufficient? This method is specially made for updating components of the main-thread.
eg:
I'm not sure how this will behave with certain gui issues (I think that most are handled by the use of synchronize). Deleting shapes might be an issue (eg if you delete the root in the example code, there will be an error, but this is just bad coding; Good coding should take such issues into account). You could also disable some gui handlings during thread update (eg AllowDeleting = false, Designing = false)
Tree.Canvas is a TCanvas3D class (the canvas is inherited from TCustomTeePanel). This is an abstract class, which handles the 3D issues which need to be drawn on the chart canvas.
TCustomTeePanel
|
TCustomTeePanelExtender
|
TCustomTree
An example is TTeeCanvas3D. TTeeCanvas3D uses a Delphi TCanvas to draw upon... You can access the Delphi TCanvas directly through the ReferenceCanvas property of TTeeCanvas3D.
I hope this info is somewhat useful to you.
Regards,
Tom.[/code]
Sorry, I'm not sure if I can provide you valuable information concerning your issues. I've forwarded your question to others who might give more information.
The latest version of TeeTree is available throught the TeeChart PRO package (starting from v7). This is still TeeTree 2.0
Isn't the synchronize method sufficient? This method is specially made for updating components of the main-thread.
eg:
Code: Select all
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, TeeProcs, TeeTree;
type
TDrawThread = class(TThread)
private
FID: Integer;
FTree: TTree;
FSleep: Cardinal;
procedure DoAddChild;
procedure DrawTree;
protected
procedure Execute; override;
public
constructor Create(ATree: TTree; ID: integer; ASleep: Cardinal);
end;
TForm1 = class(TForm)
Tree1: TTree;
Button1: TButton;
DesignMode: TButton;
AllowDeleteMode: TButton;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure DesignModeClick(Sender: TObject);
procedure Tree1DeletedShapes(Sender: TObject);
procedure AllowDeleteModeClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
{ TDrawThread }
constructor TDrawThread.Create(ATree: TTree; ID: integer; ASleep: Cardinal);
begin
FTree := ATree;
FID := ID;
FSleep:= ASleep;
FreeOnTerminate := True;
inherited Create(False);
end;
procedure TDrawThread.DoAddChild;
begin
with FTree do
begin
FTree.Roots[0].AddChild(
'Child_'+IntToStr(FTree.Roots[0].Childs.Count)
+'_Thread'+IntToStr(FID)
);
end;
end;
procedure TDrawThread.DrawTree;
begin
Synchronize(DoAddChild);
end;
procedure TDrawThread.Execute;
var i: integer;
begin
for i := 0 to 100 do
begin
DrawTree;
Sleep(FSleep);
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
TDrawThread.Create(Tree1, 1, 10000);
TDrawThread.Create(Tree1, 2, 5000);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Tree1.Designing := False;
Tree1.AllowDelete := False;
Tree1.AddRoot('Root');
Tree1.Roots[0].Expanded := True;
end;
procedure TForm1.DesignModeClick(Sender: TObject);
begin
Tree1.Designing := not Tree1.Designing;
if Tree1.Designing then
DesignMode.Caption := 'Designing ON'
else
DesignMode.Caption := 'Designing OFF'
end;
procedure TForm1.AllowDeleteModeClick(Sender: TObject);
begin
Tree1.AllowDelete := not Tree1.AllowDelete;
if Tree1.AllowDelete then
AllowDeleteMode.Caption := 'Deleting ON'
else
AllowDeleteMode.Caption := 'Deleting OFF'
end;
procedure TForm1.Tree1DeletedShapes(Sender: TObject);
begin
ShowMessage('A shape has been deleted');
end;
end.
Tree.Canvas is a TCanvas3D class (the canvas is inherited from TCustomTeePanel). This is an abstract class, which handles the 3D issues which need to be drawn on the chart canvas.
TCustomTeePanel
|
TCustomTeePanelExtender
|
TCustomTree
An example is TTeeCanvas3D. TTeeCanvas3D uses a Delphi TCanvas to draw upon... You can access the Delphi TCanvas directly through the ReferenceCanvas property of TTeeCanvas3D.
I hope this info is somewhat useful to you.
Regards,
Tom.[/code]
I did another test, by using Canvas.Lock in the thread, instead of Synchronize.
The application is not responding as long as the lock is in place, so no events are fired as well. As always, long locks should be avoided, since a non-responding application won't be very good.
The application is not responding as long as the lock is in place, so no events are fired as well. As always, long locks should be avoided, since a non-responding application won't be very good.
Code: Select all
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, TeeProcs, TeeTree;
type
TDrawThread = class(TThread)
private
FTree: TTree;
FID : String;
FSleep: Cardinal;
procedure DoAddChild;
protected
public
constructor Create(ATree: TTree; ID: String; ASleep: Cardinal);
end;
TDrawSynchronizedThread = class(TDrawThread)
private
procedure DrawTree;
protected
procedure Execute; override;
public
end;
TDrawLockedThread = class(TDrawThread)
private
protected
procedure Execute; override;
public
end;
TForm1 = class(TForm)
Tree1: TTree;
RynSynchroButton: TButton;
DesignMode: TButton;
AllowDeleteMode: TButton;
RunLockedButton: TButton;
procedure RynSynchroButtonClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure DesignModeClick(Sender: TObject);
procedure Tree1DeletedShapes(Sender: TObject);
procedure AllowDeleteModeClick(Sender: TObject);
procedure RunLockedButtonClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
{ TDrawThread }
constructor TDrawThread.Create(ATree: TTree; ID: String; ASleep: Cardinal);
begin
FTree := ATree;
FID := ID;
FSleep:= ASleep;
FreeOnTerminate := True;
inherited Create(False);
end;
procedure TDrawThread.DoAddChild;
begin
with FTree do
begin
FTree.Roots[0].AddChild(
'Child_'+IntToStr(FTree.Roots[0].Childs.Count)
+'_Thread_'+FID
);
end;
end;
{ TDrawSynchronizedThread }
procedure TDrawSynchronizedThread.DrawTree;
begin
Synchronize(DoAddChild);
end;
procedure TDrawSynchronizedThread.Execute;
var i: integer;
begin
for i := 0 to 100 do
begin
DrawTree;
Sleep(FSleep);
end;
end;
{ TDrawLockedThread }
procedure TDrawLockedThread.Execute;
var i: integer;
begin
FTree.Canvas.ReferenceCanvas.Lock; //placed here to see what happens with long lock
for i := 0 to 10 do
begin
//FTree.Canvas.ReferenceCanvas.Lock;
DoAddChild;
// FTree.Canvas.ReferenceCanvas.UnLock;
Sleep(FSleep);
end;
FTree.Canvas.ReferenceCanvas.UnLock;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Tree1.Designing := False;
Tree1.AllowDelete := False;
Tree1.AddRoot('Root');
Tree1.Roots[0].Expanded := True;
end;
procedure TForm1.DesignModeClick(Sender: TObject);
begin
Tree1.Designing := not Tree1.Designing;
if Tree1.Designing then
DesignMode.Caption := 'Designing ON'
else
DesignMode.Caption := 'Designing OFF'
end;
procedure TForm1.AllowDeleteModeClick(Sender: TObject);
begin
Tree1.AllowDelete := not Tree1.AllowDelete;
if Tree1.AllowDelete then
AllowDeleteMode.Caption := 'Deleting ON'
else
AllowDeleteMode.Caption := 'Deleting OFF'
end;
procedure TForm1.Tree1DeletedShapes(Sender: TObject);
begin
ShowMessage('A shape has been deleted');
end;
procedure TForm1.Tree1SelectShape(Sender: TTreeNodeShape);
begin
ShowMessage('A shape has been selected');
end;
procedure TForm1.RynSynchroButtonClick(Sender: TObject);
begin
TDrawSynchronizedThread.Create(Tree1, 'Sync1', 10000);
TDrawSynchronizedThread.Create(Tree1, 'Sync2', 5000);
end;
procedure TForm1.RunLockedButtonClick(Sender: TObject);
begin
TDrawLockedThread.Create(Tree1, 'Run1', 1000);
TDrawLockedThread.Create(Tree1, 'Run2', 2000);
end;
end.
-
- Newbie
- Posts: 20
- Joined: Sat Oct 04, 2003 4:00 am
Thread safety in Teetree
thanks Tom,
there is enough there to get me started .. I will post here if there are any issues or if I find other approaches
there is enough there to get me started .. I will post here if there are any issues or if I find other approaches