Learn how to a build a cloud-first strategyRegister Now

x
?
Solved

multiple tasks within the same thread

Posted on 2005-04-07
11
Medium Priority
?
201 Views
Last Modified: 2010-08-05
hey all,
I have made a thread class to be used within a multi-threaded queue based application.
dozens of tasks are inserted in a listview queue, and depending on how many threads are created by the user the tasks are carried out.
its runs nicely except my code is hard to follow and read. There are several different tasks a thread can run so I was thinking about separating each task into a different unit instead of all the code being inside one unit.

Should I create a thread class for each type of task? Or just use one thread class?

I have tried breaking up my code into separate units for each task while still using one thread... but Im having probelms with "Synchronize".
How can I "Synchronize" a thread from a different unit, even if the thread is being used within that units code.

Should I just keep all my thread code within the same unit?
Instead of trying to make it more readable...
thoughts?
Paul
0
Comment
Question by:LMuadDIb
  • 5
  • 5
11 Comments
 
LVL 6

Expert Comment

by:vadim_ti
ID: 13734214
May be you can create CommonTaskThread class with functionality common for all tasks and to save it in separate unit.

After it inherit thread for every task from CommonTaskThread and save every such thread in a separate unit

0
 
LVL 27

Assisted Solution

by:BigRat
BigRat earned 800 total points
ID: 13735689
First every task is a class derived from TThread.

>>dozens of tasks are inserted in a listview queue...

implies that there is code which updates this view, irrespective of what the task really does. This implies a base class (from TThread) which contains this code.

>>There are several different tasks a thread can run so I was thinking about separating each task into a different unit instead of all the code being inside one unit.

Yes, I do exactly the same. Each worker task is in its own unit and is a class derived from the base class. (In my case it is an IDE supporting several langaues. I compile and run the programs using threads. TCustomCompiler is my base class, TJavaScriptCompiler, TRatScriptCompiler etc.. are derived from it).

>>Should I create a thread class for each type of task?
Yes, derived from your base class.

>>How can I "Synchronize" a thread from a different unit, even if the thread is being used within that units code.

This statement implies that two tasks might be started, or that a task starts another, and that somewhere one task has to wait for another. It might even imply that two tasks have to wait for a common event. Could you be a little more explicit about this? The Synchronize method is really about executing something on the main thread - usually to update the GUI which is NOT thread safe. Other threads are still running inspite of the fact that one thread executes a synchronize.


0
 
LVL 4

Author Comment

by:LMuadDIb
ID: 13753988
"Synchronize" is used to display status and log errors if any. Its also used to adjust a cust progressbar.
I adjusted my code, now my base thread class deals only with the queue and deciding which Job/Task to execute.
I moved all my Job/Task code to separate units and they are derived from my base class.

here is my base thread class:

//---------------------------
unit WorkerThread;
//....
type
  TQueuedItem = record  //used to hold a listview items data, to be passed to the thread
    ID: integer;                 // cannot pass item, sense we delete the item right away
    Job: string;
    Status: string;
    Misc: string;
  end;

type
  TWorkerThread = class(TThread)
  private
    FJob: string;
    FJobID: integer;
    FJobError: string;
    FJobStatus: string;
    FJobCaption: string;
    FAbort: boolean;
    FPause : boolean;

    FQueuedItem: TQueuedItem;

    FProgressBar: TCustomProgressBar;
    FProgBarMaxValue: Integer;
    FProgBarProgress: Integer;

    procedure GrabJobFromQueue;

  protected
    procedure Execute; override;
    constructor Create();
    //destructor  Destroy;
  public

    property ProcessFile: string read FJob write FJob;
    property JobError: string read FJobError write FJobError;
    property JobStatus: string read FJobStatus write FJobStatus;
    property JobCaption: string read FJobCaption write FJobCaption;
    property Abort: boolean read FAbort write FAbort;
    property Pause: boolean read FPause write FPause;

    property QueuedItem: TQueuedItem read FQueuedItem write FQueuedItem;

    property ProgBarMaxValue: Integer read FProgBarMaxValue write FProgBarMaxValue;
    property ProgBarProgress: Integer read FProgBarProgress write FProgBarProgress;
    property ProgressBar: TCustomProgressBar
             read FProgressBar write FProgressBar;

    procedure DoLog;
    procedure DoLogErr;
    procedure DoAddJobToResults;

  end;

implementation

uses uMain, WThrd_GrpList;

{ TWorkerThread }

constructor TWorkerThread.Create();
begin
  inherited Create ( false);
  Priority := tpLowest;
  FreeOnTerminate := true;
end;

{destructor TWorkerThread.Destroy;
begin
  inherited Destroy;
end; }

procedure TWorkerThread.DoLog;
begin
  fMain.LogInfo(FJobStatus, false);  //display info in memo log, true = err
end;

procedure TWorkerThread.DoLogErr;
begin
  fMain.LogInfoQueue(FJobError, true);  //display info in memo queue log, true = err
end;

procedure TWorkerThread.DoAddJobToResults;
begin
  fMain.AddJobToResults(FJobCaption, FJobError);  // job/task complete
end;

procedure TWorkerThread.GrabJobFromQueue;
{ HERE WE GRAB AN ITEM FROM THE QUEUE AND DELETE !!! }
begin
  FQueuedItem := fMain.Pop;  // grab item and delete the lv item too here

  FJob:= FQueuedItem.Job;
  FJobID:= FQueuedItem.ID;
  FJobStatus:= FQueuedItem.Status;
  FJobCaption:= FQueuedItem.Misc;

  FJobError:= '';
  FAbort:= false;
  FPause:= false;
  FProgBarMaxValue:=0;
  FProgBarProgress:=0;
end;

procedure TWorkerThread.Execute;
begin
  inherited;

  while not Terminated do
  begin

    Synchronize(GrabJobFromQueue);

    PostMessage(fMain.Handle,UM_SETTEXT,
      integer(pointer(FProgressBar)),Integer(pchar(FJob)));

    if FJob ='UPDATE GROUP' then begin
      FJobCaption := 'UPDATE GROUP - ' + FJobCaption;
      JobGetGrpList.DoUpDateGroupList;
    end;

    if fMain.ThreadCount > fMain.MaxThreads then
      terminate;
  end;
end;

//----------------------------------------------------------

but in my worker task Im having problems accessing the procedures and such in my base class
to use "DoLogErr" in my base class from my worker class using  do I use the following?

JobGetGrpList.JobError := inttostr(GroupCount) + ' groups on news server.'  ;
 Synchronize(TWorkerThread(JobGetGrpList).DoLogErr);

or

JobGetGrpList.JobError := inttostr(GroupCount) + ' groups on news server.'  ;
Synchronize(JobGetGrpList.DoLogErr);

another way? Neither seem to work, nothing is displayed in memo logs that is.

here is some code from a worker task unit:
//---------------------------------------------------

unit WThrd_GrpList;

//...

type
  TJobGetGroup = class(TWorkerThread)
  private
    GroupCount: integer;
    GroupName: string;
    GroupID: integer;

    ProgBarMaxValue : integer;
    ProgBarProgress : integer;

  protected
    procedure GrabNewsGroupList(nntp1: TIdNNTP);
  public
    GroupList: TStrings;

    constructor Create();
    destructor  Destroy;
    procedure DoUpDateGroupList;

    // --- THE idNTTP EVENTS ---------
    procedure IdNNTPStatus(ASender: TObject; const AStatus: TIdStatus;
      const AStatusText: String);
    procedure IdNNTPNewsgroupList(const ANewsgroup: String; const ALow,
      AHigh: Integer; const AType: String; var ACanContinue: Boolean);

  end;

var
  JobGetGrp: TJobGetGroup;

implementation

uses
  uMain;

constructor TJobGetGroup.Create();
begin
  inherited;
  GroupList := TStringList.Create;
end;

destructor TJobGetGroup.Destroy;
begin
  JobGetGrpList.Free;
end;

procedure TJobGetGroup.DoUpDateGroupList;
var
  nntp: TIdNNTP;
begin
  //-------------------------------
  nntp := TIdNNTP.Create(nil);
  try
          try
            //-----------------------------
            GrabNewsGroupList(nntp);
            //-----------------------------
          finally
            JobGetGrpList.JobError := inttostr(GroupCount) + ' groups on news server.'  ;
            Synchronize(TWorkerThread(JobGetGrpList).DoLogErr);
            Disconnect;
          end;
  finally
    nntp.free;
    Synchronize(TWorkerThread(JobGetGrpList).DoAddJobToResults);
  end;

end;

procedure TJobGetGroup.GrabNewsGroupList(nntp1: TIdNNTP);
begin
  JobGetGrpList := TJobGetGroupList.Create;
  try
    with nntp1 do begin
      OnStatus := IdNNTPStatus;
      OnNewsgroupList := IdNNTPNewsgroupList;
      //-----------------------------------------------------
      TJobGetGroupList(JobGetGrpList).GroupCount := 0;
      TJobGetGroupList(JobGetGrpList).ProgBarMaxValue := 25000;
      TJobGetGroupList(JobGetGrpList).ProgBarProgress := 0;
      //-----------------------------------------------------
      PostMessage(fMain.Handle, UM_SetMax,
          integer(pointer(JobGetGrpList.ProgressBar)),
          TJobGetGroupList(JobGetGrpList).ProgBarMaxValue);
      PostMessage(fMain.Handle,UM_SETTEXT,
          integer(pointer(JobGetGrpList.ProgressBar)),
          Integer(pchar('Updating GroupList...')));
      //-----------------------------------------------------
    end;
    try

      nntp1.GetNewsgroupList;
    finally
      GroupList.SaveToFile('c:\test.txt');   //FCacheFileName
      GroupList.Free;

    end;

  finally
    JobGetGrpList.Free;
  end;

end;

//------------------------------------------------------------------------------

procedure TJobGetGroup.IdNNTPStatus(ASender: TObject; const AStatus: TIdStatus;
  const AStatusText: String);
begin
  JobGetGrpList.JobStatus := AStatusText;
  Synchronize(JobGetGrpList.DoLog);
end;


procedure TJobGetGroupList.IdNNTPNewsgroupList(const ANewsgroup: String; const ALow,
  AHigh: Integer; const AType: String; var ACanContinue: Boolean);
begin  // used ONLY by NewsGroupList
  inc(TJobGetGroup(JobGetGrpList).ProgBarProgress);
  inc(TJobGetGroup(JobGetGrpList).GroupCount);

  TJobGetGroup(JobGetGrpList).GroupList.Add('1,,' + ANewsGroup + ',1,' + IntToStr(AHigh-ALow) + ',0,0,0,' + IntToStr(ALow) + ',' + IntToStr(AHigh));

end;

end.

//---------------------------------------------------

tia
Paul
0
Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

 
LVL 6

Expert Comment

by:vadim_ti
ID: 13754935
why not simple:

           Synchronize(DoLogErr);
 
or i missed something?
0
 
LVL 4

Author Comment

by:LMuadDIb
ID: 13755441
I get access violations if I just use

Synchronize(DoLogErr);

 instead of

Synchronize(TWorkerThread(JobGetGrp).DoLogErr);

but I think it has to do with creation of the worker task- JobGetGrp: TJobGetGroup;
where should I "JobGetGrp := TJobGetGroup.create..."
should I create the task in my base thread class?
0
 
LVL 6

Expert Comment

by:vadim_ti
ID: 13755656
I cannot see where you define JobGetGrp
I think you need to define it as protected member of your base thread class
0
 
LVL 4

Author Comment

by:LMuadDIb
ID: 13758142
but if I put JobGetGrp in the protected section of the base thread class, "WorkerThread" unit needs to be added to the USES interface.
and "WorkerThread" unit is also in the USES interface of the WThrd_GrpList unit. This casues  an error "[Fatal Error] WorkerThread.pas(7): Circular unit reference to 'WorkerThread'"

both units need WorkerThread unit in the USES interface section and not in implementation
0
 
LVL 6

Expert Comment

by:vadim_ti
ID: 13758179
In any case you need to add "WorkerThread" unit in  interface uses clause of WThrd_GrpList , but
i do not see any reason why you need in WorkerThread
uses WThrd_GrpList
could you please post
your interface and implementation uses clauses of these two units
0
 
LVL 4

Author Comment

by:LMuadDIb
ID: 13765031
unit WorkerThread is my base thread class which holds TWorkerThread = class(TThread)
unit WThrd_GrpList holds my derived class TJobGetGroup = class(TWorkerThread)

yes,
"WorkerThread" unit needs to be in the interface uses of the WThrd_GrpList sense the TJobGetGroup class is dervived from TWorkerThread.
If I define JobGetGrp as a protected member of my base class, then I need "WThrd_GrpList" unit to the interface USES of my base class unit "WorkerThread"... which cause the Circular unit Error.

//------------------------------------
unit WorkerThread;

interface

uses classes, SysUtils, windows, CustProgBar, messages, ComCtrls,
  IdBaseComponent, IdComponent, IdTCPConnection, IdNNTP, WThrd_GrpList,
  IdTCPClient, IdMessageClient, IdException;

type
  TQueuedItem = record
    ID: integer;
    Job: string;
    Status: string;
    Misc: string;
  end;

type
  TWorkerThread = class(TThread)
  private
    FJob: string;
    FJobID: integer;
    FJobError: string;
    FJobStatus: string;
    FJobCaption: string;
    FAbort: boolean;
    FPause : boolean;
    FQueuedItem: TQueuedItem;
    FProgressBar: TCustomProgressBar;
    FProgBarMaxValue: Integer;
    FProgBarProgress: Integer;
    procedure GrabJobFromQueue;
  protected
    procedure Execute; override;
    constructor Create();
    //destructor  Destroy;

    JobGetGroup: TJobGetGroupList;
  public

    property ProcessFile: string read FJob write FJob;
    property JobError: string read FJobError write FJobError;
    property JobStatus: string read FJobStatus write FJobStatus;
    property JobCaption: string read FJobCaption write FJobCaption;
    property Abort: boolean read FAbort write FAbort;
    property Pause: boolean read FPause write FPause;

    property QueuedItem: TQueuedItem read FQueuedItem write FQueuedItem;

    property ProgBarMaxValue: Integer read FProgBarMaxValue write FProgBarMaxValue;
    property ProgBarProgress: Integer read FProgBarProgress write FProgBarProgress;
    property ProgressBar: TCustomProgressBar
             read FProgressBar write FProgressBar;

    procedure DoLog;
    procedure DoLogErr;
    procedure DoAddJobToResults;

  end;

implementation

uses uMain;

{ TWorkerThread }

//------------------------------------
//------------------------------------
//------------------------------------

unit WThrd_GrpList;

interface

uses
  classes, SysUtils, IdBaseComponent, IdComponent, IdTCPConnection, IdNNTP,
  IdTCPClient, IdMessageClient, IdException, messages, windows, WorkerThread,
  Dialogs;

type
  TJobGetGroup = class(TWorkerThread)
  private
    GroupCount: integer;
    GroupName: string;
    GroupID: integer;
    ProgBarMaxValue : integer;
    ProgBarProgress : integer;
  protected
    procedure GrabNewsGroupList(nntp1: TIdNNTP);
  public
    GroupList: TStrings;
    constructor Create();
    destructor  Destroy;
    procedure DoUpDateGroupList;
    // --- THE idNTTP EVENTS ---------
    procedure IdNNTPStatus(ASender: TObject; const AStatus: TIdStatus;
      const AStatusText: String);
    procedure IdNNTPNewsgroupList(const ANewsgroup: String; const ALow,
      AHigh: Integer; const AType: String; var ACanContinue: Boolean);
  end;

implementation

// Uses - No uses here


-----------------------
thanks for helping
Paul
0
 
LVL 6

Accepted Solution

by:
vadim_ti earned 1200 total points
ID: 13765116
where is TJobGetGroupList defined ? and waht it is?

can you place declaration of this type into interface part of WorkerThread unit? It can to solve your problem
0
 
LVL 4

Author Comment

by:LMuadDIb
ID: 13774130
TJobGetGroupList is actually TJobGetGroup just misspelled, but it still wont fix the problem

I have tried somehting different though.
Instead of creating a derived class from my WorkerThread, I created a new class which will be my base class which TJobGetGroup will be derived from.

so TWorkerThread will work with the Queue, and my new class TWorkerJob will hold all the task subclasses that I need
I will then define the TWorkerJob to the TWorkerThread. I least I hope :)

Thanx for all the help vadim_ti & BigRat ...
I have learned alot from this.......

Paul
0

Featured Post

VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

Question has a verified solution.

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

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…
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…
Are you ready to place your question in front of subject-matter experts for more timely responses? With the release of Priority Question, Premium Members, Team Accounts and Qualified Experts can now identify the emergent level of their issue, signal…
Look below the covers at a subform control , and the form that is inside it. Explore properties and see how easy it is to aggregate, get statistics, and synchronize results for your data. A Microsoft Access subform is used to show relevant calcul…
Suggested Courses
Course of the Month20 days, 15 hours left to enroll

864 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