Question

Boss Thread and worker threads communication

Asked by: jaja2005

Question: Hi. In my main application (main VCL) I want to develop a single boss
thread which:

A: is able to communicate with worker threads
B: is able to update some VCL componets properties into main VCL basing
on the worker thread results.
C: create the worker thread and limit the number of it using a semaphore

Worker threads are I/O operation. They do some test, create a sort of
array of boolean based on the test performed and store into a shared
buffer (es. TthreadList).

The approach I would like to use is the one of producer/consumer.

So the sequence should be:

- The boss spaws some worker threads
- The workers threads do some test and create a result as [TRUE,FALSE,FALSE...]
  with a size that is dynamic.

- the worker thread store it into a shared buffer, protected with Critical Section.

- The boss thread wakes up or wait to access to the buffer. When it does it
will update the propreties in the main VCL


I want to develop this structure without using Syncronize and above all

limiting the buffer size and taking care of flow control between boss an
workers.

This Question has been solved and asker verified All Experts Exchange premium technology solutions are available to subscription members.

Subscribe now for full access to Experts Exchange and get

Instant Access to this Solution

  • Plus...
  • 30 Day FREE access, no risk, no obligation
  • Collaborate with the world's top tech experts
  • Unlimited access to our exclusive solution database
  • Never be left without tech help again

Subscribe Now

Asked On
2009-07-07 at 13:49:04ID24551096
Tags

Delphi 2006

Topics

Delphi Components

,

Delphi Programming

Participating Experts
2
Points
500
Comments
22

Trusted by hundreds of thousands everyday for fast, accurate and reliable tech support.

  • "The time we save is the biggest benefit of Experts Exchange to Warner Bros. What could take multiple guys 2 hours or more each to find is accessed in around 15 minutes on Experts Exchange." Mike Kapnisakis, Warner Bros.
  • "Our team likes having a resource that is more secure than just using Google and most experts using this service really know their stuff. It's nice to look here first versus using Google." Dayna Sellner, Lockheed Martin
  • "Anytime that I've been stumped with a problem, 9 out of 10 times Experts Exchange has either the accepted solution or an open discussion of the potential solution to the problem." Kenny Red, eBay Inc.

See what Experts Exchange can do for you.

Got a question?

We've got the answer.

Experts Exchange has been collecting answers to technology questions since 1996…3 million and counting! If you have a question, chances are we already have your answer.

Screenshot of Experts Exchange Knowledgebase

Need individual assistance?

Our experts are ready to help.

If you can't find the exact answer you're looking for, ask our exclusive community of 50,000 experts. You’ll get a personalized answer from a trusted professional.

Screenshot of Experts Exchange Knowledgebase

Want to learn from the best?

Read articles from industry experts.

Thousands of free tech tips, tricks, how-to’s and tutorials are available in our peer reviewed articles section. See for yourself how smart our experts are, no login required.

Screenshot of an Article

Working on a long term project?

Store your work and research.

Save solutions to your questions, answers you’ve discovered through searching plus helpful articles in your personal knowledgebase for easy future access.

Screenshot of Experts Exchange Knowledgebase

Access the answers to your technology questions today.

Subscribe Now

30-day free trial. Register in 60 seconds.

What Makes Experts Exchange Unique?

Members of the expert community talk about why the experience at Experts Exchange is different than what you will find anywhere else.

Trusted by the world's most respected brands.

image of each brand's logo

Faithfully serving IT professionals since 1996.

Experts Exchange Logo

Try it out and discover for yourself.

Subscribe Now

30-day free trial. Register in 60 seconds.

Related Solutions

  1. Communication from controller thread to worker thread
    I have created a SDI class in my Visual C++ Studio 5.0. Under the View class, I have begun a worker thread called worker() by using AfxBeginThread(). Also, I have passed the pointer to the View class for the worker thread to pass the messages to controller thread. However,...
  2. worker thread updating an object created in main thread
    I am having some problems with a simple main thread + single worker thread application .. they may be due to my logic or perhaps to a misunderstanding of thread communication. I cannot reduce the problem to a simple example as yet, so I am looking for conceptual feedback. I ...

Free Tech Articles

  1. WARNING: 5 Reasons why you should NEVER fix a computer for free.
    It is in our nature to love the puzzle. We are obsessed. The lot of us. We love puzzles. We love the challenge. We thrive on finding the answer. We hate disarray. It bothers us deep in our soul. W...
  2. SCCM OSD Basic troubleshooting
    SCCM 2007 OSD is a fantastic way to deploy operating systems, however, like most things SCCM issues can sometimes be difficult to resolve due to the sheer volume of logs to sift through and the dispe...
  3. Migrate Small Business Server 2003 to Exchange 2010 and Windows 2008 R2
    This guide is intended to provide step by step instructions on how to migrate from Small Business Server 2003 to Windows 2008 R2 with Exchange 2010. For this migration to work you will need the fo...
  4. Create a Win7 Gadget
    This article shows you how to create a simple "Gadget" -- a sort of mini-application supported by Windows 7 and Vista. Gadgets can be dropped anywhere on the desktop to provide instant information, ...
  5. Outlook continually prompting for username and password
    There have been a lot of questions recently regarding Outlook prompting for a username and password whilst using Exchange 2007. There are a few reasons why this would happen and I will try to cover t...
  6. Backup Exchange 2010 Information Store using Windows Backup
    There seems to be quite a lot of confusion around the ability to backup Exchange 2010 using the built in Windows Backup feature. This stems from the omission of this feature prior to Exchange 2007 s...

Cloud Class Webinars

  1. Avoiding Bugs in Microsoft Access
    Alison Balter takes and in-depth look at avoiding bugs in Access. In this webinar you will learn about using the immediate window to debug your applications, invoking the debugger, using breakpoints to troubleshoot, stepping through code, setting the next statement to execute, ...
  2. Top 10 Best New Features in Visio 2010
    Scott Helmers gives live demonstrations of the top 10 new features in Visio 2010. This webinar will teach you how to create compelling diagrams by adding shapes to the page with a single click, linking the shapes in a diagram to data in Excel (or SQL Server, or SharePoint), ...
  3. IT Consultant Business Secrets Revealed
    Michael Munger, Experts Exchange tech pro and IT consultant, pulls back the curtain on his very successful businesses and answers question on every IT consultant and business owner should know about. He shares secrets on what he did to solve the 5 most common problems in IT, ...
  4. Disaster Recovery and Business Continuity
    Quest CTO, Mike Billon, gives an overview of the steps involved in building a dunamic disaster recovery plan. Through case studies and an examination of software/hardware tooles for monitoring and testing, you'll gain a better understandin of where you are, where you want ...
  5. Organize Your Visio Diagrams with Containers and Lists
    Scott Helmers uses cross functional flowcharts, wireframe diagrams, data graphic legends and seating charts to teach you: how to ustilize all three new structured diagram components in Visio 2010, the best practices for organizeing shapes in previous version of Visio, how to organize ...
  6. How to Us Objects, Properties, Events and Methods in Microsoft Access
    Alison Dalter gives an in-depbth look at objects, properties, events and methods in Microsoft Access. In this webinar you will learn about using the object browser, referring to objects, working with properties and methods, working with object variables, understanding the ...

Join the Community

Give a Little. Get a Lot.

Join the community of experts here and help other tech pros by answering question in your area of expertise. You can earn FREE access to all Experts Exchange's premium features and resources.

Join the Community

Answers

 

by: Geert_GruwezPosted on 2009-07-07 at 19:27:09ID: 24800111

m2c

a thread shouldn't update anything in the VCL if not using synchronize or lock
instead you should let the form update itself (not in a thread)
rather give the form the information it needs in a object instance and send it a message the data changed

 

by: dougaugPosted on 2009-07-08 at 14:24:38ID: 24808528

Hi, jaja2005,

I've made a little project for you (but I haven't finished the tests yet). See if it is something like this that you want.

To compile it, create the files which name are surrounding by '*******'.

Regards,

Douglas.

************************
 TestThread.dpr
***********************
program ThreadTest;
 
uses
  Forms,
  Mainform in 'Mainform.pas' {FMainform},
  BossWorkerThreads in 'BossWorkerThreads.pas';
 
{$R *.res}
 
begin
  Application.Initialize;
  Application.CreateForm(TFMainform, FMainform);
  Application.Run;
end.
 
 
********************************
 Mainform.dfm
********************************
 
object FMainform: TFMainform
  Left = 192
  Top = 103
  Width = 696
  Height = 480
  Caption = 'Thread Test'
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OldCreateOrder = False
  OnClose = FormClose
  PixelsPerInch = 96
  TextHeight = 13
  object Button1: TButton
    Left = 32
    Top = 24
    Width = 113
    Height = 25
    Caption = 'Create Boss Thread'
    TabOrder = 0
    OnClick = Button1Click
  end
  object Memo1: TMemo
    Left = 24
    Top = 64
    Width = 257
    Height = 321
    TabOrder = 1
  end
  object Button2: TButton
    Left = 328
    Top = 24
    Width = 169
    Height = 25
    Caption = 'Resume Boss Thread'
    TabOrder = 2
    OnClick = Button2Click
  end
  object Button3: TButton
    Left = 168
    Top = 24
    Width = 153
    Height = 25
    Caption = 'Suspend Boss Thread'
    TabOrder = 3
    OnClick = Button3Click
  end
end
 
********************************
 Mainform.pas
********************************
unit Mainform;
 
interface
 
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;
 
type
  TFMainform = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    Button2: TButton;
    Button3: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure Button3Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
 
var
  FMainform: TFMainform;
 
implementation
 
{$R *.dfm}
 
uses BossWorkerThreads;
 
var
  b: TBossThread;
procedure TFMainform.Button1Click(Sender: TObject);
begin
  b := TBossThread.Create(3, Self);
end;
 
procedure TFMainform.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  if b <> nil then
    b.Terminate;
end;
 
procedure TFMainform.Button3Click(Sender: TObject);
begin
  if b <> nil then
    b.Suspend;
end;
 
procedure TFMainform.Button2Click(Sender: TObject);
begin
  if b <> nil then
    b.Resume;
end;
 
end.
 
****************************
BossWorkerThreads.pas
****************************
unit BossWorkerThreads;
 
interface
 
uses
  SysUtils, Classes, SyncObjs, Windows, Mainform, Dialogs;
 
type
  TWorkerThread = class;
 
  TWorkerThreadResult = record
    WorkerThread: TWorkerThread;
    WorkerThreadID: Cardinal;
    Result: Boolean;
  end;
 
  PWorkerThreadResult = ^TWorkerThreadResult;
 
  TBossThread = class(TThread)
  private
    FFMainform: TFMainform;
    FSemaphore: Integer;
    FEvent: TEvent;
    FWorkerResults: TThreadList;
    FWorkerThreadList: TList;
    { Private declarations }
  protected
    procedure Execute; override;
    function SpawnThread: Boolean;
  public
    constructor Create(Semaphore: Integer; FMainform: TFMainform);
    destructor Destroy; override;
    procedure AddWorkerThreadResult(WorkerThreadResult: TWorkerThreadResult);
    property Event: TEvent read FEvent;
  end;
 
  TWorkerThread = class(TThread)
  private
    FBossThread: TBossThread;
    { Private declarations }
    function DoTest(TestFlag: Boolean): Boolean;
  protected
    procedure Execute; override;
  public
    constructor Create(BossThread: TBossThread);
  end;
 
implementation
 
{ TBossThread }
 
procedure TBossThread.AddWorkerThreadResult(WorkerThreadResult: TWorkerThreadResult);
begin
  with FWorkerResults do
  begin
    with LockList do
    try
       Add(@WorkerThreadResult);
       FEvent.SetEvent;
    finally
       UnlockList;
    end;
  end;
end;
 
constructor TBossThread.Create(Semaphore: Integer; FMainform: TFMainform);
begin
  inherited Create(True);
  FWorkerResults := TThreadList.Create;
  FWorkerThreadList := TList.Create;
  FEvent := TEvent.Create(nil, False, False, '');
  FSemaphore := Semaphore;
  FFMainform := FMainform;
  FreeOnTerminate := True;
  Resume;
end;
 
destructor TBossThread.Destroy;
var
  i: Integer;
begin
  for i := 0 to FWorkerThreadList.Count - 1 do
    TWorkerThread(FWorkerThreadList[i]).Terminate;
 
  FWorkerResults.Free;
  FEvent.Free;
  inherited;
end;
 
procedure TBossThread.Execute;
var
  List: TList;
  WorkerThreadResult: PWorkerThreadResult;
  FAvailableThreads: Boolean;
begin
  while not Terminated do
  begin
    repeat
      FAvailableThreads := SpawnThread
    until not FAvailableThreads;
 
    List := FWorkerResults.LockList;
 
    try
      if List.Count > 0 then
      begin
        WorkerThreadResult := List.Items[0];
        FFMainform.Memo1.Lines.Add('ThreadID = ' + IntToStr(WorkerThreadResult^.WorkerThreadID) + ', ' +
                                   'Result = ' + IntToStr(Integer(WorkerThreadResult^.Result)));
        FWorkerResults.Remove(WorkerThreadResult);
      end
    finally
      FWorkerResults.UnlockList;
 
      if List.Count = 0 then
      begin
        FEvent.ResetEvent;
        FEvent.WaitFor(INFINITE);
      end;
    end;
  end;
end;
 
function TBossThread.SpawnThread: Boolean;
begin
  Result := False;
 
  if FSemaphore > 0 then
  begin
    FWorkerThreadList.Add(TWorkerThread.Create(Self));
    Dec(FSemaphore);
    Result := True;
  end;
end;
 
{ TWorkerThread }
 
constructor TWorkerThread.Create(BossThread: TBossThread);
begin
  inherited Create(True);
  FBossThread := BossThread;
  FreeOnTerminate := True;
  Resume;
end;
 
function TWorkerThread.DoTest(TestFlag: Boolean): Boolean;
begin
  Result := not TestFlag;
end;
 
procedure TWorkerThread.Execute;
var
  WorkerThreadResult: TWorkerThreadResult;
  TestFlag: Boolean;
begin
  TestFlag := False;
 
  with WorkerThreadResult do
  begin
    WorkerThread := Self;
    WorkerThreadID := ThreadID;
  end;
 
  while not Terminated do
  begin
    TestFlag := DoTest(TestFlag);
    WorkerThreadResult.Result := TestFlag;
    FBossThread.AddWorkerThreadResult(WorkerThreadResult);
  end;
end;
 
end.
                                              
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
159:
160:
161:
162:
163:
164:
165:
166:
167:
168:
169:
170:
171:
172:
173:
174:
175:
176:
177:
178:
179:
180:
181:
182:
183:
184:
185:
186:
187:
188:
189:
190:
191:
192:
193:
194:
195:
196:
197:
198:
199:
200:
201:
202:
203:
204:
205:
206:
207:
208:
209:
210:
211:
212:
213:
214:
215:
216:
217:
218:
219:
220:
221:
222:
223:
224:
225:
226:
227:
228:
229:
230:
231:
232:
233:
234:
235:
236:
237:
238:
239:
240:
241:
242:
243:
244:
245:
246:
247:
248:
249:
250:
251:
252:
253:
254:
255:
256:
257:
258:
259:
260:
261:
262:
263:
264:
265:
266:
267:
268:
269:
270:
271:
272:
273:
274:
275:
276:
277:
278:
279:
280:
281:
282:
283:
284:
285:
286:
287:
288:
289:
290:
291:
292:
293:
294:
295:
296:
297:
298:
299:
300:
301:
302:
303:
304:
305:
306:
307:
308:
309:
310:
311:
312:
313:

Select allOpen in new window

 

by: jaja2005Posted on 2009-07-08 at 22:37:23ID: 24810964

Good day dougaug!
Great, many thanks.  I am going to study and test it and let you know my doubts.
See ya

 

by: jaja2005Posted on 2009-07-08 at 23:02:00ID: 24811024

Just one thing i have noticed. You use TestFlag: Boolean a single variable while i want to work with an array or list of bolean. Let me explain you my goal. At beginning the BossThread copies a sort of  
array or list of control to be performed (maybe via worker constructor) kind of:
 
A (String) - B (char or string) - C (String or Integer)  
An example:
Check if  ValueofTemperature is > of 30
 
ValueofTemperature is 'A'
'>' is B
30 is 'C'
 
So each worker thread will get its tasks to be completed from the BossThread. One workerthread may receive 10 controls to verify other less or maybe more. So the workerthread would create a sort
of array or TList of Bolean of results internally. Once the thread is done itwill inform the boss thread(maybe with postmessage) and copies the TList in a shared buffer (maybe before the thread destroy itself (OnTerminate=True)).  Now the Bossthread may picks up an item from the buffer ( where a single item is a array of TList of boolean seen before) and upadate a TListBox or whatever in my VCL main thread.

Hope it helps.
Thx

 

 

by: dougaugPosted on 2009-07-09 at 12:44:25ID: 24817487

Ok I have understood. I will try to do something like you said and post the code here.

Regards

 

by: jaja2005Posted on 2009-07-09 at 13:06:23ID: 24817773

Great, in meanwhile i will study the previous example
Thanks a lot for your help!

 

by: Geert_GruwezPosted on 2009-07-10 at 00:38:10ID: 24821034

dougaug, jaja2005
let me point out some things:
first of all,
you have circular unit reference :(  
this should by avoided between the mainform and the bossthread

second, you are updating the VCL from within a other thread !
this will hang up your app !!!

it's a nice start, but unforatunately will provide some problems
i'll rebuild it for you so you can use the bossthread from any form !
and show you how to do a callback to a higher object

check this article on to use a callback:
http://www.experts-exchange.com/articles/Programming/Languages/Pascal/Delphi/Displaying-progress-in-the-main-form-from-a-thread-in-Delphi.html
it works for any form
this works with a synchronised callback, but we can change this too
i will use a object to return info

let me work on this a little ...

 

by: Geert_GruwezPosted on 2009-07-10 at 04:44:01ID: 24822199

i got something like this:

unit frmMain;
 
interface
 
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls;
 
type
  TformMain = class(TForm)
    memInfo: TMemo;
    lbxThreads: TListBox;
    pnlOptions: TPanel;
    btnPauseResume: TButton;
    btnStart: TButton;
    btnStop: TButton;
    btnAddWorker: TButton;
    procedure btnAddWorkerClick(Sender: TObject);
    procedure btnStartClick(Sender: TObject);
  private
    fNumber: Integer;
    procedure ReturningInfo(Sender: TObject; Msg: TStrings);
  public
    constructor Create(AOwner: TComponent); override;
  end;
 
var
  formMain: TformMain;
 
implementation
 
uses threadBoss;
 
{$R *.dfm}
 
type
  TCountingThread = class(TWorkerThread)
  protected
    procedure DoAction; override;
  end;
 
procedure TformMain.btnAddWorkerClick(Sender: TObject);
var x: TTask;
begin
  x := TTask.Create;
  x.WorkerThreadClass := TCountingThread;
  x.Name := 'Worker' + IntToStr(fNumber);
  lbxThreads.Items.Values[x.Name] := ';
  AddTask(x);
  Inc(fNumber);
end;
 
{ TCountingThread }
 
procedure TCountingThread.DoAction;
var I, n: Integer;
 
begin
  I := 0;
  n := Random(10) + 3;
  repeat
    Sleep(1000);
    SignalStatus(IntToStr(I));
    Inc(I);
  until I >= n;
end;
 
procedure TformMain.ReturningInfo(Sender: TObject; Msg: TStrings);
var I: Integer;
begin
  memInfo.Lines := Msg;
  with lbxThreads.Items do
    for I := 0 to Count - 1 do
      Values[Names[I]] := Msg.Values[Names[I]+'_STATUS'];
end;
 
procedure TformMain.btnStartClick(Sender: TObject);
begin
  StartBoss(ReturningInfo);
  btnAddWorker.Enabled := True;
end;
 
constructor TformMain.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  fNumber := 1;
end;
 
end.

                                              
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:

Select allOpen in new window

 

by: Geert_GruwezPosted on 2009-07-10 at 04:47:37ID: 24822221

and the threadboss unit:

(note that the frmMain is not in the uses Clauses of the threadboss)
it's not completed, but it should give you an idea of which way to go

unit threadBoss;
 
interface
 
uses Classes, SysUtils, SyncObjs;
 
{$WRITEABLECONST ON}
 
const
  AutoStartBossThread: Boolean = True;
 
type
  TWorkerThread = class;
  TTask = class;
 
  TTaskStatus = (tsNotStarted, tsInit, tsRunning, tsFinished, tsError);
 
  TSignalToBossEvent = procedure (Sender: TObject; TaskName: string; WorkerMsg: TStrings) of object;
 
  TTaskItem = class(TCollectionItem)
  private
    fTask: TTask;
    fStatus: TTaskStatus;
    fThread: TWorkerThread;
    fSignalToBoss: TSignalToBossEvent;
  protected
    procedure ThreadSignal(Sender: TObject; Msg: TStrings);
  public
    constructor Create(Collection: TCollection); override;
    destructor Destroy; override;
  end;
 
  TTaskItems = class(TCollection)
  private
    function GetItems(Index: Integer): TTaskItem;
  protected
  public
    constructor Create;
    function Add: TTaskItem;
    property Items[Index: Integer]: TTaskItem read GetItems;
  end;
 
 
  TSignalBossEvent = procedure (Sender: TObject; Msg: TStrings) of object;
  TBossThread = class(TThread)
  private
    fMaxWorkers: Integer;
    fWorkers: TThreadList;
    fEvent: TEvent;
    fSignalMsgEvent: TSignalBossEvent;
    fMsg: TStrings;
    procedure CheckNewTasks;
    procedure StartTask(aTaskItem: TTaskItem);
  protected
    procedure DoSignal; virtual;
    procedure Signal; virtual;
    procedure OnThreadMsg(Sender: TObject; TaskName: string; WorkerMsg: TStrings);
    procedure Execute; override;
    procedure ThreadTerminated(Sender: TObject);
  public
    constructor Create(CreateSuspended: Boolean; aSignalMsgEvent: TSignalBossEvent = nil; aMaxWorkers: integer = 5);
    destructor Destroy; override;
    class procedure Warning;
  end;
 
  TSignalWorkerEvent = procedure (Sender: TObject; Msg: TStrings) of object;
  TWorkerThread = class(TThread)
  private
    fMsgInfo: TStrings;
    fSignalMsgEvent: TSignalWorkerEvent;
    fUseSynchronize: Boolean;
  protected
    procedure CreateMsgInfo; virtual;
    procedure Execute; override;
    procedure DoSignal; virtual;
    procedure Signal; virtual;
    procedure SignalBegin; virtual;
    procedure SignalEnd; virtual;
    procedure SignalError(aMsg: string); virtual;
    procedure SignalStatus(aMsg: string); virtual;
  public
    constructor Create(CreateSuspended: Boolean; aSignalMsgEvent: TSignalWorkerEvent = nil; aUseSynchronize: boolean = True); reintroduce;
    destructor Destroy; override;
    procedure DoAction; virtual;
  end;
 
  TWorkerThreadClass = class of TWorkerThread;
 
  TTask = class(TObject)
  private
    fWorkerThreadClass: TWorkerThreadClass;
    fName: string;
  protected
  public
    property Name: string read fName write fName;
    property WorkerThreadClass: TWorkerThreadClass read fWorkerThreadClass write fWorkerThreadClass;
  end;
 
procedure AddTask(aTask: TTask);
procedure StartBoss(ReturnInfoEvent: TSignalBossEvent = nil);
 
implementation
 
var
  mTasks: TTaskItems;
  mTaskCs: TCriticalSection;
  mBoss: TBossThread;
 
procedure StartBoss(ReturnInfoEvent: TSignalBossEvent = nil);
begin
  if not Assigned(mBoss) then
    mBoss := TBossThread.Create(False, ReturnInfoEvent);
end;
 
procedure InitTasks;
begin
  mTasks := TTaskItems.Create;
  mTaskCs := TCriticalSection.Create;
end;
 
procedure DoneTasks;
begin
  FreeAndNil(mTaskCs);
  FreeAndNil(mTasks);
end;
 
procedure AddTask(aTask: TTask);
begin
  if Assigned(aTask) then
  begin
    mTaskCs.Enter;
    try
      with mTasks.Add do
        fTask := aTask;
      TBossThread.Warning;
    finally
      mTaskCs.Leave;
    end;
  end;
end;
 
{ TTaskItem }
 
constructor TTaskItem.Create(Collection: TCollection);
begin
  inherited Create(Collection);
  fTask := Nil;
  fStatus := tsNotStarted;
end;
 
destructor TTaskItem.Destroy;
begin
  FreeAndNil(fTask);
  inherited Destroy;
end;
 
procedure TTaskItem.ThreadSignal(Sender: TObject; Msg: TStrings);
var msgStatus: string;
begin
  msgStatus := Msg.Values['STATUS'];
  if msgStatus = 'BEGIN' then
    fStatus := tsInit
  else if msgStatus = 'STATUS' then
    fStatus := tsRunning
  else if msgStatus = 'END' then
    fStatus := tsFinished
  else if msgStatus = 'ERROR' then
    fStatus := tsError;
  if Assigned(fSignalToBoss) then
    fSignalToBoss(Sender, fTask.Name, Msg);
end;
 
{ TTaskItems }
 
function TTaskItems.Add: TTaskItem;
begin
  Result := TTaskItem(inherited Add);
end;
 
constructor TTaskItems.Create;
begin
  inherited Create(TTaskItem);
end;
 
function TTaskItems.GetItems(Index: Integer): TTaskItem;
begin
  Result := TTaskItem(inherited Items[Index]);
end;
 
{ TBossThread }
 
constructor TBossThread.Create(CreateSuspended: Boolean; aSignalMsgEvent: TSignalBossEvent = nil; aMaxWorkers: integer = 5);
begin
  inherited Create(CreateSuspended);
  fMaxWorkers := aMaxWorkers;
  fWorkers := TThreadList.Create;
  fEvent := TEvent.Create(nil, False, False, 'thread_boss');
  fSignalMsgEvent := aSignalMsgEvent;
  fMsg := TStringList.Create;
end;
 
destructor TBossThread.Destroy;
begin
  FreeAndNil(fMsg);
  FreeAndNil(fEvent);
  FreeAndNil(fWorkers);
  inherited Destroy;
end;
 
procedure TBossThread.DoSignal;
begin
  Synchronize(Signal);
end;
 
procedure TBossThread.Signal;
begin
  if Assigned(fSignalMsgEvent) then
    fSignalMsgEvent(Self, fMsg);
end;
 
procedure TBossThread.StartTask(aTaskItem: TTaskItem);
begin
  with fWorkers.LockList do
  try
    if Count < fMaxWorkers then
    begin
      with aTaskItem do
      begin
        fSignalToBoss := OnThreadMsg;
        fStatus := tsInit;
        fThread := fTask.fWorkerThreadClass.Create(True, aTaskItem.ThreadSignal);
        fThread.FreeOnTerminate := True;
        fThread.OnTerminate := ThreadTerminated;
        Add(fThread);
        fThread.Resume;
      end;
    end;
  finally
    fWorkers.UnlockList;
  end;
end;
 
procedure TBossThread.ThreadTerminated(Sender: TObject);
begin
  with fWorkers.LockList do
  try
    Delete(IndexOf(Sender));
  finally
    fWorkers.UnlockList;
  end;
end;
 
procedure TBossThread.CheckNewTasks;
var I: Integer;
begin
  mTaskCs.Enter;
  try
    for I := 0 to mTasks.Count - 1 do
      if mTasks.Items[I].fStatus in [tsNotStarted] then
      begin
        StartTask(mTasks.Items[I]);
        Break;
      end;
  finally
    mTaskCs.Leave;
  end;
end;
 
procedure TBossThread.Execute;
begin
  while not Terminated do
  begin
    case fEvent.WaitFor(200) of
      wrSignaled    : ;
      wrTimeout     : CheckNewTasks;
      wrAbandoned   : ;
      wrError       : ;
      wrIOCompletion: ;
    end;
  end;
end;
 
procedure TBossThread.OnThreadMsg(Sender: TObject; TaskName: string; WorkerMsg: TStrings);
var I: Integer;
begin
  for I := 0 to WorkerMsg.Count - 1 do
    fMsg.Values[TaskName + '_' + WorkerMsg.Names[I]] := WorkerMsg.ValueFromIndex[I];
  DoSignal;
end;
 
class procedure TBossThread.Warning;
begin
  if not Assigned(mBoss) and AutoStartBossThread then
    StartBoss;
  if Assigned(mBoss) then
    mBoss.fEvent.SetEvent;
end;
 
{ TWorkerThread }
 
constructor TWorkerThread.Create(CreateSuspended: Boolean; aSignalMsgEvent: TSignalWorkerEvent = nil; aUseSynchronize: boolean = True);
begin
  inherited Create(CreateSuspended);
  CreateMsgInfo;
  fSignalMsgEvent := aSignalMsgEvent;
  fUseSynchronize := aUseSynchronize;
end;
 
procedure TWorkerThread.CreateMsgInfo;
begin
  fMsgInfo := TStringList.Create;
end;
 
destructor TWorkerThread.Destroy;
begin
  FreeAndNil(fMsgInfo);
  inherited Destroy;
end;
 
procedure TWorkerThread.DoAction;
begin
  // override this method in descendant
end;
 
procedure TWorkerThread.Execute;
begin
  SignalBegin;
  try
    try
      DoAction;
    except
      on E: Exception do
        SignalError(E.Message);
    end;
  finally
    SignalEnd;
  end;
end;
 
procedure TWorkerThread.Signal;
begin
  if Assigned(fSignalMsgEvent) then
    fSignalMsgEvent(Self, fMsgInfo);
end;
 
procedure TWorkerThread.DoSignal;
begin
  if fUseSynchronize then
    Synchronize(Signal)
  else
    Signal;
end;
 
procedure TWorkerThread.SignalBegin;
begin
  fMsgInfo.Values['STATUS'] := 'BEGIN';
  DoSignal;
end;
 
procedure TWorkerThread.SignalEnd;
begin
  fMsgInfo.Values['STATUS'] := 'END';
  DoSignal;
end;
 
procedure TWorkerThread.SignalError(aMsg: string);
begin
  fMsgInfo.Values['STATUS'] := 'ERROR';
  fMsgInfo.Values['ERRORMSG'] := aMsg;
  DoSignal;
end;
 
procedure TWorkerThread.SignalStatus(aMsg: string);
begin
  fMsgInfo.Values['STATUS'] := 'STATUS';
  fMsgInfo.Values['STATUSMSG'] := aMsg;
  DoSignal;
end;
 
initialization
  InitTasks;
finalization
  DoneTasks;
end.

                                              
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
159:
160:
161:
162:
163:
164:
165:
166:
167:
168:
169:
170:
171:
172:
173:
174:
175:
176:
177:
178:
179:
180:
181:
182:
183:
184:
185:
186:
187:
188:
189:
190:
191:
192:
193:
194:
195:
196:
197:
198:
199:
200:
201:
202:
203:
204:
205:
206:
207:
208:
209:
210:
211:
212:
213:
214:
215:
216:
217:
218:
219:
220:
221:
222:
223:
224:
225:
226:
227:
228:
229:
230:
231:
232:
233:
234:
235:
236:
237:
238:
239:
240:
241:
242:
243:
244:
245:
246:
247:
248:
249:
250:
251:
252:
253:
254:
255:
256:
257:
258:
259:
260:
261:
262:
263:
264:
265:
266:
267:
268:
269:
270:
271:
272:
273:
274:
275:
276:
277:
278:
279:
280:
281:
282:
283:
284:
285:
286:
287:
288:
289:
290:
291:
292:
293:
294:
295:
296:
297:
298:
299:
300:
301:
302:
303:
304:
305:
306:
307:
308:
309:
310:
311:
312:
313:
314:
315:
316:
317:
318:
319:
320:
321:
322:
323:
324:
325:
326:
327:
328:
329:
330:
331:
332:
333:
334:
335:
336:
337:
338:
339:
340:
341:
342:
343:
344:
345:
346:
347:
348:
349:
350:
351:
352:
353:
354:
355:
356:
357:
358:
359:
360:
361:
362:
363:
364:
365:
366:
367:
368:
369:
370:
371:
372:
373:
374:
375:
376:
377:
378:
379:
380:
381:
382:
383:
384:

Select allOpen in new window

 

by: jaja2005Posted on 2009-07-10 at 04:59:54ID: 24822312

Thank guys! Well it seems that I will have lot of thing to read before my holiday...:-)
see ya

 

by: jaja2005Posted on 2009-07-13 at 13:54:27ID: 24843825

Hi Geert, I am studyng you code and note all the question or doubts I have.
Should I expect some other code from you?

>> it's not completed....

see u.

 

by: jaja2005Posted on 2009-07-13 at 13:56:55ID: 24843848

ah..i forgot to tell u. I will be on holiday next 15 days.
If I will not renew the service now can I do it later on and access again to my open question and continue?

Thx

 

by: Geert_GruwezPosted on 2009-07-15 at 11:36:55ID: 24862582

i'll be on holiday too for 3 weeks
i'll be working on this project for myself
i don't know when exactly it will be finished

i'll post the changes in the future, possibly in a article too

 

by: jaja2005Posted on 2009-07-15 at 11:44:03ID: 24862668

ok see you soon and enjoy your holiday.

 

by: jaja2005Posted on 2009-09-18 at 08:37:53ID: 25367082

Hi All. I am back.
I have been testing your code. I have a couple of question.

As Task I want to ping a node and get the result. I've added
a new components for this in TWorkerThread class as below:

constructor TWorkerThread.Create(CreateSuspended: Boolean; aSignalMsgEvent: TSignalWorkerEvent = nil; aUseSynchronize: boolean = True);
begin
 inherited Create(CreateSuspended);
 CreateMsgInfo;
 fSignalMsgEvent := aSignalMsgEvent;
 fUseSynchronize := aUseSynchronize;
 FPing := TipwPing.Create(nil);
with FPing do begin
 PacketSize := 32;
 Timeout := 60; // Abandon ping attempt after 10 second
 //Idle := True;
 Tag := 0;
 OnResponse := PingThreadResponse;
 FOnError := PingThreadError;
end;


procedure TWorkerThread.PingThreadResponse(Sender: TObject; RequestId: Integer;
     const ResponseSource, ResponseStatus: string; ResponseTime: Integer);
     begin
     FThreadPingResult := ResponseStatus;
     if (ResponseStatus = 'OK') then
     SignalStatus(' Node is UP');
     SignalEnd;
     end;

procedure TWorkerThread.DoAction;
begin
FPing.PingHost('127.0.0.1');
end;

procedure TWorkerThread.Execute;
begin
 SignalBegin;
   try
    try
   DoAction;
   except
 on e: Exception do
       SignalError (e.Message);// Always fired????
    end;
    finally
    SignalEnd;
end;
end;


FPing components works like that:

if Timeout is >0 the component blocks. If the Timeout property is set to 0, all operations return immediately, potentially  failing with an error if they can't be completed immediately.

- If I specify an IP not reachable then i get correctly Timeout message with SignalError (e.Message) after 10s.
- If I specify a valid IP i.e. 127.0.0.1 I got:

- The message "Node is UP" but only after "Timeout seconds"  
- I get also the message Timeout 301 along with "Node is UP", exception is always fired.

Any suggestion?
What's wrong?
:-(


Thx

























 

by: jaja2005Posted on 2009-09-19 at 00:14:32ID: 25371855

Want to be sure to undestand you code.
Please check the steps below;

1. First you inizialize the TCollection (TTaskItems) and the Critical Section objects. At very first time the Tcollection is empty.
2. You start the BossThread and in the constructor you set some fSignalMsgEvent to point to ReturningInfo in MainForm.  the fevent is created.

3. BossThread starts chekings for newtask (CheckNewTasks) to be excute in TCollection (TTaskItems). for it you use wrTimeout which value can be easly changed.

4. By Clicking on AddWorker you create an instace of TTask class and sets fStatus in tsNotStarted Status.

5. By using AddTask(x) you add a new TTaskItem to TTaskItems with this code:

with mTasks.Add do
        fTask := aTask;
       TBossThread.Warning;

mTasks.Add automactly creates an object of TTaskItem. You copy x in fTask, which is private field of TTaskItem. if I understood  correctly your approach the real job to be done is specified by ftask field right? Have seen other example where TTaskItem was create like:

TPingItem = class (TCollectionItem)
public
PingThreadId : Integer;
PingThread : TPingThread;
...

In the example above PingThead has events to be fired like PingOnReply, PinOnStatus and so on...

Your decided to use class reference in order to change the type of WorkerThread so job to be done
in DoAction right?

 

by: Geert_GruwezPosted on 2009-09-19 at 11:32:00ID: 25373950

i stopped my further work on the thread boss and worker
i'm gonna use this for myself:
http://otl.17slon.com/

 

by: jaja2005Posted on 2009-09-20 at 03:28:45ID: 25376569

Hi. Hmm, I've studied deeply your code and i would like to have things working. The project works nice unless i use in DoAction a sample task (as you have done by using a repeat-until loop). As you can see I added a  FPing component as private filed in WorkerThread. The result I got is described above.

ID: 25367082

The example you posted it has been very useful to me to start to undestand the mechanims of exchanging signal among theads, I have to admit. I am very interested in that and learn a good approach for running task and having work done within a workerthread back to mainVCL or BossThread using delphi events. The structure would be very useful to be implemented
in different scenario where task might be everything.

Why you moved to OmniThreadLibrary? Have you been able to get the same result with Omni? Can you post the code for the example of lunching task from a BossThread?

Do you have examples project on this topic?

I was wating for an article of your on that..:-((

thx





 

by: jaja2005Posted on 2009-09-20 at 04:39:07ID: 25376818

Hi Geert.
It must be something wrong with FPing components. Indy Ping works nice.
Let you know.

 

by: Geert_GruwezPosted on 2009-09-20 at 23:39:18ID: 25380616

i bumped into more problems creating this unit
some things i couldn't solve

amongst others, i allways had to recreate the contructor to implement different parameters

i was looking for a uniform way to do this and went googling
and found OmniThread

My current project involves monitoring some 100+ oracle databases
Currently i'm looping 1 database at a time, checking options and executing commands
so the next database needs to wait on all the tasks to finish of the previous one

with omnithread it looks like i can create a worker for each task
then the pool will run the tasks with a max fixed number of threads
and start a new workers as a thread finishes
so for max = 10, i would be running 10 tasks simultaneously

the next thing is, they are creating a connection pool
also very cool

the unit i created is very basic, and i'm gonna learn this new one
with the knowledge i get from this, i should be able to get more into it



 

by: jaja2005Posted on 2009-09-22 at 14:17:59ID: 25397713

I use Delphi 2006., probably it won't work. I have compiled the package and have now in palette only TOmniEventMonitor...it's all that I need...? :-(

 

by: Geert_GruwezPosted on 2009-09-22 at 20:37:38ID: 25399842

lol, same here, threads are not visual components with events to attach
it's possible it will never be more on the palette

20120131-EE-VQP-002

3 Ways to Join

30-Day Free Trial

The Experts

98% positive feedback on 31,087 answers since March 2000. angeliii is a Microsoft Most Valuable Professional for his work with MS SQL Server & Develoment.

He has also proven his knowledge of Visual Basic Programming, PHP Scripting and Oracle Databases.

The Experts

97% positive feedback on 10,752 answers since July 2000. lrmoore has more than 18 years experience in the networking industry.

The six-time Mircosoft MVPs specialties include firewalls, virtual private networking, and network management.

Testimonials

"...and excellent source for support... Kind of like having your very own IT dept." Electriciansnet

Testimonials

"I was apprehensive at signing up at first. However... it has already made my life as an IT administrator much easier." JaCrews

Testimonials

"WOW! You guys have great, active, and knowledgeable people on here." moore50

Business Clients

Business Clients

In the Press

"If you’ve got a question... Experts Exchange can supply an answer.”

In the Press

"...an invaluable aid for both IT professionals and those who require tech support."

In the Press

"where IT professionals provide quick answers on just about any topic"

Business Account Plans

Loading Advertisement...