Solved

Help with Threads

Posted on 2006-11-24
8
421 Views
Last Modified: 2010-04-05
I am having my first foray into using threads, but I am coming unstuck when trying to use sql in one of the threads. I suspect That I am not creating an instance  of the database connection.

Also I think I am also have a problem destroying the threads when I close the App. Can you Help?

Main Thread
===========================================
procedure TMyDataParse1.execute;
var
   p: String;
   EndStr, StrCheck: integer;
begin
            Regex1 := TPerlRegEx.Create(nil);
            Regex1.RegEx := RegExpStr1;
            Regex1.Subject := FDataString;
              if Regex1.Match then begin
              StrCheck := 1;
                  if RegExpEnd1 = '1' then begin
                  EndStr := 1;
                  StrCheck := 0;
                  end
                  else
                  Endstr := 0;
                  StrCheck :=1;
              end;
               if RegExpEnd1 <> '1' then begin
                Regex1.RegEx := RegExpEnd1;
                Regex1.Subject := FDataString;;
                if Regex1.Match then begin
                EndStr := 1;
                StrCheck := 0;
                end
                else begin
                EndStr := 0;
                StrCheck := 1;
                end;
               end;


                if EndStr = 1 then begin
                sysStr1 := sysStr1+FDataString;
                CallString := CallConv(sysStr1,PhonesysID1);
                CallString := CallString+',1,1';
                Synchronize(SendCSVData);
                EndStr := 0;
                SysStr1 := '';
                StrCheck := 0;
                end
                else begin
                  if StrCheck = 1 then
                  SysStr1 := SysStr1+FDataString+#13#10;
                end;
end;

procedure TMyDataParse1.SendCSVData;
 begin
 TMyDataCalcuLate1.Create(CallString); // send data to new thread
 Form1.SQLOutData(CallString);
  end;
===================================================

TMyDataCalcuLate1.pas thread
===================================================
uses
Windows, Messages, SysUtils, Classes, Forms, Dialogs, DateUtils,StdCtrls,DB,passqlite, passql, ComCtrls;

Type
TMyDataCalcuLate1=class(TThread)
  private
  ThreadCollexDB : TliteDB;
  FCalString: String;
  SiteID,SysTimeBandStart, SysTimeBandEnd, SysOffset, SysTOffset :String;
  CallInsert : Pchar;
  public
  constructor Create(CalString:String);
  procedure execute; override;
  function split(Text : string; Token : char ; WordNo : integer):string;
  Procedure SendSql;
end;

implementation
uses
Unit1;
constructor TMyDataCalcuLate1.Create(CalString: String);
begin
  FreeOnTerminate :=True;
  FCalString := CalString;
  inherited Create(False);
end;


Procedure TMyDataCalcuLate1.Execute;
var
debug : integer;
begin
Try
debug := 0;
  With ThreadCollexDB do
  Begin
  ThreadCollexDB := TliteDB.Create(nil, ExtractFilePath(Application.ExeName)+'www\collex.sdb');
  end;
SIteID := '1';

 {**************************************************
 Get System setting from Database
 ***************************************************}
 ThreadCollexDB.Query('SELECT * from sys_col where col_active = 1 and col_id ='''+SiteID+'''');
 if  ThreadCollexDB.RowCount > 0 then begin
 SysTimeBandStart := ThreadCollexDB.Results[0][7];
 SysTimeBandEnd := ThreadCollexDB.Results[0][8];
 end;
end;
end
===================================================


0
Comment
Question by:lloydie-t
  • 3
  • 3
8 Comments
 
LVL 17

Expert Comment

by:Wim ten Brink
Comment Utility
You might need to initialize the COM functionality in your code, like this:

(uses ActiveX)

Procedure TMyDataCalcuLate1.Execute;
begin
  CoInitializeEx(nil, 0); // <-------------------Add this.
  try
    <Your code here>
  finally CoUnInitialize; // <-------------------Add this.
  end;
end;

Not sure if your database relies on COM in some places, though so I might be mistaken.
0
 

Author Comment

by:lloydie-t
Comment Utility
No COM here using passqlite to access sqlite database
0
 
LVL 17

Expert Comment

by:Wim ten Brink
Comment Utility
But what about TPerlRegEx? And are you sure that there are no COM interfaces used in PasSQLLite?
0
Enabling OSINT in Activity Based Intelligence

Activity based intelligence (ABI) requires access to all available sources of data. Recorded Future allows analysts to observe structured data on the open, deep, and dark web.

 

Author Comment

by:lloydie-t
Comment Utility
I am fairly Positive. PasSqlite.pas is a delphi wrapper which uses sqlite3.dll.
On a normal form I would do.

  public
    { Public declarations }
    LiteDB1 : Tlitedb;

blah
.........
.....
begin
With LiteDB1 do
                Begin
                LiteDB1 := TliteDB.Create(self, ExtractFilePath(Application.ExeName)+'www\collex.sdb3');
                end;

do some sqlite stuff
.............
............

LiteDB1.close
end.

0
 

Author Comment

by:lloydie-t
Comment Utility
Doh. Wrong Database name! I am still have a few problems when I close the app. I suspect I am opening a thread but not closing all the threads which are open. Do I need to force 'terminate'?

Do I need to create a destructor for each thread?
0
 
LVL 17

Accepted Solution

by:
Wim ten Brink earned 125 total points
Comment Utility
You don't always need a destructor but if you do create one, do remember that it is executed in the same thread as the thread itself or in the thread that calls it's Free method. Personally, it's one reason why I don't like the TThread class that much and prefer to use the BeginThread API instead. :-) It's more complex and requires more experience but it also tends to be a bit more reliable in the end.
 
Furthermore, another problem might arise when you close your application and all it's threads. It could be that your main thread has already destroyed all it's objects but one of the other threads might still be running, trying to access data from one of the main thread objects. Basically, your main thread should notify all other threads to close and then actually wait until they're all closed before you can continue to free any components from your main thread.

Personally, I generally use threads in the following way:
-------------------------------------------------------
unit frmMain;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    ListBox1: TListBox;
    procedure FormCreate(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    ThreadHandle: THandle;
    ThreadID: Cardinal;
    ThreadTerminate: Boolean;
  protected
    procedure Execute;
  public
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

function MyThreadFunc(lpThreadParameter: Pointer): Integer; stdcall;
var
  MyForm1: TForm1 absolute lpThreadParameter;
begin
  try
    TForm1(lpThreadParameter).Execute;
  finally
    Result := 0;
  end;
end;

procedure TForm1.Execute;
begin
  while not ThreadTerminate do
  begin
    ListBox1.Items.Add(FormatDateTime('HH:NN:SS.ZZZ', Now));
    Sleep(500);
  end;
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  ThreadTerminate := True;
  Sleep(0);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  ThreadTerminate := False;
  IsMultiThread := True;
  ThreadHandle := CreateThread(nil, 0, @MyThreadFunc, Pointer(Self), CREATE_SUSPENDED, ThreadID);
end;

procedure TForm1.FormDestroy(Sender: TObject);
var
  ExitCode: DWORD;
begin
  while not GetExitCodeThread(ThreadHandle, ExitCode) do
    Sleep(100);
end;

procedure TForm1.FormShow(Sender: TObject);
begin
  ResumeThread(ThreadHandle);
end;

end.
-------------------------------------------------------
object Form1: TForm1
  Caption = 'Form1'
  OldCreateOrder = False
  OnClose = FormClose
  OnCreate = FormCreate
  OnDestroy = FormDestroy
  OnShow = FormShow
  object ListBox1: TListBox
    Left = 8
    Top = 8
    Width = 200
    Height = 593
    Font.Charset = DEFAULT_CHARSET
    Font.Color = clWindowText
    Font.Height = -11
    Font.Name = 'Courier New'
    Font.Style = []
    ItemHeight = 14
    ParentFont = False
    TabOrder = 0
  end
end
-------------------------------------------------------

The advantage of the CreateThread/BeginThread API is that you can use a very short function to let the thread call back a method on your form or datamodule or whatever else. The drawback is that you will encounter minor issues with synchronisation between main and child threads. So it requires a lot of experience with threads to make this work right.
The reason for not using BeginThread in this sample is simple, btw. I created this one with Delphi 6 and Delphi 6 is messing with the thread parameter. It's wrapping it up in a TThreadRec structure which is declared in the Sysem unit in the implementation part so it's not even accessible from the outside. Not a good thing! So I just explicitly tell Delphi that I'm going to use multiple threads (IsMultiThread := True) and then just use the Windows API.

Since the thread method is part of the form itself, I also happen to have easy access to any of it's properties. Just keep in mind that you might want to protect these properties to avoid multiple threads from modifying the same value at the same time. Which, as I said, is the more complicated synchronisation issue. You just don't have the Synchronise method that TThread offers...

Anyway, you should check the code of your threads making sure that they always check if it should terminate even from within the tiniest loops. But also keep in mind that some database actions might take a while to finish and during this time it could be that you don't have any chance to check if the thread should terminate or not. You could call the TerminateThread() API with the thread handle to try and force termination of the thread but that's not always very reliable and might result in data loss.
0
 
LVL 1

Expert Comment

by:Computer101
Comment Utility
Forced accept.

Computer101
EE Admin
0

Featured Post

Threat Intelligence Starter Resources

Integrating threat intelligence can be challenging, and not all companies are ready. These resources can help you build awareness and prepare for defense.

Join & Write a Comment

Creating an auto free TStringList The TStringList is a basic and frequently used object in Delphi. On many occasions, you may want to create a temporary list, process some items in the list and be done with the list. In such cases, you have to…
In my programming career I have only very rarely run into situations where operator overloading would be of any use in my work.  Normally those situations involved math with either overly large numbers (hundreds of thousands of digits or accuracy re…
Illustrator's Shape Builder tool will let you combine shapes visually and interactively. This video shows the Mac version, but the tool works the same way in Windows. To follow along with this video, you can draw your own shapes or download the file…
Access reports are powerful and flexible. Learn how to create a query and then a grouped report using the wizard. Modify the report design after the wizard is done to make it look better. There will be another video to explain how to put the final p…

743 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

18 Experts available now in Live!

Get 1:1 Help Now