PatV 1 Posted July 15, 2019 (edited) Found the prob.... it's too quick, I need to introduce a delay in my loop and everything is working now if you have any suggestions, you're welcome Hi All, I'm using Delphi 10.3 As testing, I've a main form when I click on MyForm Create Thread Button, I launch the code bellow ; I create a form TForm.Create(Self) and I include a frame in it TFrm.Create(Self) GetTGUIDString simply convert number from TGIUID.NewGuid.ToString to letter, and remove special char to get a random name. In My Frame, I have defined a Thread ; TThDb = class(TThread) private FOwner : TComponent; FDM : TFDb; // is a datamodule who will be created within the tread FEvent : TEvent; FError : TFError; FRet : TFRet; FAgs : string; FWhenDone : PrcDone; procedure InitializeDatabase; procedure ConnectToDatabase; Procedure WorkOnData; function LoadData : TThDb; procedure Execute; override; public constructor Create(bSuspended : boolean); destructor Destroy; override; property Event : TEvent read FEvent write FEvent; function WhenDone(aValue : PrcDone) : TThDb; function WithAgencies(aValue : string) : TThDb; function WithOwner(aValue : TComponent) : TThDb; procedure Start; // initialise the connection to the database the first time procedure ReStart; // update the variable in the query/procedure end; {------------------------------------------------------------------------------} procedure TTHDB.InitializeDatabase;begin CoInitialize(Nil); FDM := TFDb.Create(nil); FDM.Connection.ReadOnly:=True; CoUninitialize;end; {------------------------------------------------------------------------------} procedure TTHDB.ConnectToDatabase;var rParam : rConnectionConfig; begin rParam := GetConnectionConfig; rParam.ModuleName := GetTGUIDString; FDM.InitConnection(rParam); FDM.Connect;end; {------------------------------------------------------------------------------} procedure TThDb.ReStart; begin synchronize( procedure() begin LoadData.WorkOnData; end);end; {------------------------------------------------------------------------------} Procedure TTHDB.WorkOnData; begin if Assigned(FWhenDone) then FWhenDone(FRet); Application.ProcessMessages;end; {------------------------------------------------------------------------------} function TThDb.LoadData : TThDb; var aFiles : TFFilesStatus; begin ... some work ... FRet.FValue:=TValue.From<TFFilesStatus>(aFiles); FRet.FId := FAgs; result:=Self;end; {------------------------------------------------------------------------------} procedure TThDb.Start; begin Resume;end; {------------------------------------------------------------------------------} procedure TThDb.Execute; begin InitializeDatabase; ConnectToDatabase; restart;end; {------------------------------------------------------------------------------} {------------------------------------------------------------------------------} {------------------------------------------------------------------------------} and in the frame constructor I have constructor TFrm.Create(aOwner: TComponent);begin inherited Create(aOwner); FError := TFError.Create; FTh:=TThDb.Create(True); FTh.WithOwner(Self).WhenDone(UpdateGrid);end; and launch the following code ; var i : integer; begin FTh.Start; i:=1; while True do with FTh do if Event.waitfor(0)=wrSignaled then begin Event.ResetEvent; WithAgencies(Format('%0.2d',)).ReStart; inc(i); if i>99 then i:=1; if Terminated then Break; end else Application.ProcessMessages; FTh.Free; As you see I've a loop ( it's nomal in my test), now If I click again on MyForm Create Thread Button from the main form, I create another TForm with a TFrrame in, and the thread connected to the database from the previous form/frame stop working. Is it a normal behaviour or am I missing something ? What I would like of course is that both form with thread in are still running. Thanks for your help Patrick Edited July 15, 2019 by PatV correction Share this post Link to post
PeterBelow 238 Posted July 15, 2019 If you want to use a database that requires COM (e.g. via dbGo) inside a thread you have to call CoInitialize at the start of the execute method and CoUninitialize at the end. You are not doing that, your InitializeDatabase method calls CoUnInitialize, so any subsequent use of the database connection may not work as intended. You thread Execute method does not contain a loop, after one pass through the code the thread will exit. Also keep in mind that database connections are usually bound to the thread that created them, so using a dataset bound to the connection created by the thread in another thread (e.g. in the main thread through a Synchronize call) may either not work or involve some internal thread synchronization done by the framework you are using (COM objects using appartment threading are such a case) that may block until the code returns to the message loop. Something else that may foul your design: Creating a datamodule inside a thread is not a problem per se, but if you have design-time links of components on forms, frames, other datamodules to stuff on this datamodule (or vice versa) the way the VCL streaming mechanism resolves them may torpedo your intent. This mechanism is not thread-safe, and it resolves the links using the Name of the component linked to, and it looks for the container (form etc.) in a global list, where it will only find the first created instance of the container, even if you have created several. Share this post Link to post
PatV 1 Posted July 15, 2019 Thanks a lot Peter, I will check the code the follow your advice. Share this post Link to post
John Kouraklis 94 Posted July 15, 2019 Also see this https://chee-yang.blogspot.com/2015/12/delphi-multi-threading_4.html#remove-completed-itask-instance-reference-from-task-list The example with the dataset Share this post Link to post