Want to win a PS4? Go Premium and enter to win our High-Tech Treats giveaway. Enter to Win

x
?
Solved

Help with Threads

Posted on 2006-11-24
8
Medium Priority
?
431 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
Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

 

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 500 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

Industry Leaders: 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!

Question has a verified solution.

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

Introduction The parallel port is a very commonly known port, it was widely used to connect a printer to the PC, if you look at the back of your computer, for those who don't have newer computers, there will be a port with 25 pins and a small print…
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…
In this video, Percona Solutions Engineer Barrett Chambers discusses some of the basic syntax differences between MySQL and MongoDB. To learn more check out our webinar on MongoDB administration for MySQL DBA: https://www.percona.com/resources/we…
We’ve all felt that sense of false security before—locking down external access to a database or component and feeling like we’ve done all we need to do to secure company data. But that feeling is fleeting. Attacks these days can happen in many w…
Suggested Courses

636 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