Solved

Help with Threads

Posted on 2006-11-24
8
426 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 3
  • 3
8 Comments
 
LVL 17

Expert Comment

by:Wim ten Brink
ID: 18006858
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
ID: 18007339
No COM here using passqlite to access sqlite database
0
 
LVL 17

Expert Comment

by:Wim ten Brink
ID: 18008142
But what about TPerlRegEx? And are you sure that there are no COM interfaces used in PasSQLLite?
0
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 

Author Comment

by:lloydie-t
ID: 18008277
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
ID: 18008861
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
ID: 18083871
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
ID: 21135797
Forced accept.

Computer101
EE Admin
0

Featured Post

Free Tool: IP Lookup

Get more info about an IP address or domain name, such as organization, abuse contacts and geolocation.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

A lot of questions regard threads in Delphi.   One of the more specific questions is how to show progress of the thread.   Updating a progressbar from inside a thread is a mistake. A solution to this would be to send a synchronized message to the…
This article explains how to create forms/units independent of other forms/units object names in a delphi project. Have you ever created a form for user input in a Delphi project and then had the need to have that same form in a other Delphi proj…
This video Micro Tutorial shows how to password-protect PDF files with free software. Many software products can do this, such as Adobe Acrobat (but not Adobe Reader), Nuance PaperPort, and Nuance Power PDF, but they are not free products. This vide…
Monitoring a network: why having a policy is the best policy? Michael Kulchisky, MCSE, MCSA, MCP, VTSP, VSP, CCSP outlines the enormous benefits of having a policy-based approach when monitoring medium and large networks. Software utilized in this v…

729 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