OmniThreadLibrary forum
News: SMF - Just Installed!
 
*
Welcome, Guest. Please login or register. May 17, 2012, 06:08:34 PM


Login with username, password and session length


Pages: [1]   Go Down

Author Topic: Problem with GlobalOmniThreadPool.CountExecuting  (Read 212 times)

Qmodem

  • Newbie
  • *
  • Posts: 9
    • View Profile
    • Email
Problem with GlobalOmniThreadPool.CountExecuting
« on: January 26, 2012, 10:29:56 AM »

I have been following the progress of OTL and finally decided to get my feet wet with threading.  I created a simple example that uses ADO and followed all the rules about creating the connection inside the thread.  That works fine.  However, the problem I am having is when the "go" button is pressed, it falls right through the test of GlobalOmniThreadPool.CountExecuting.  If the worker threads are still hanging around and I press the "go" button again, it works as it should and waits till the two tasks are complete before passing.  If I wait for the worker threads to expire and then press "go", it falls right past the test again.

Here is the code:

Code: [Select]
procedure TCCC100.LogMessages(s : string);
begin
  Memo1.Lines.Add(s);
end;


procedure TCCC100.LoadInventoryRecords(const task : IOmniTask);
begin
  task.Comm.Send(1);
  Sleep(500);
end;


procedure TCCC100.LoadPartRecords(const task : IOmniTask);
begin
  task.Comm.Send(1);
  Sleep(500);
end;


procedure TCCC100.Button1Click(Sender : TObject);
const
  MSG_START = 1;
begin
  Button1.Enabled := false;
  Memo1.Clear;
  Memo1.Update;
  CreateTask(LoadInventoryRecords, 'LoadInventoryRecords')
    .OnMessage(
      procedure(const task : IOmniTaskControl; const msg : TOmniMessage)
        var taskNumber : Integer;
      begin
        if msg.MsgID = MSG_START then
          begin
            taskNumber := task.Param['TaskNumber'].AsInteger;
            LogMessages(Format('task Loading Inventory - %d / %d start', [taskNumber, task.UniqueID]));
          end;
      end)
    .OnTerminated(
      procedure(const task : IOmniTaskControl)
      begin
        LogMessages(Format('%d Inventory records loaded.', [0{RS_Inventory.RecordCount}]));
      end)
    .SetParameter('TaskNumber', 1)
    .Schedule(GlobalOmniThreadPool);

  Application.ProcessMessages;

  CreateTask(LoadPartRecords, 'LoadPartRecords')
    .OnMessage(
      procedure(const task : IOmniTaskControl; const msg : TOmniMessage)
        var taskNumber : Integer;
      begin
        if msg.MsgID = MSG_START then
          begin
            taskNumber := task.Param['TaskNumber'].AsInteger;
            LogMessages(Format('task Loading Parts - %d / %d start', [taskNumber, task.UniqueID]));
          end;
      end)
    .OnTerminated(
      procedure(const task : IOmniTaskControl)
      begin
        LogMessages(Format('%d part records loaded.', [0{RS_Part.RecordCount}]));
      end)
    .SetParameter('TaskNumber', 2)
    .Schedule(GlobalOmniThreadPool);

  Application.ProcessMessages;

  //wait all finished
  while GlobalOmniThreadPool.CountExecuting + GlobalOmniThreadPool.CountQueued > 0 do
    Application.ProcessMessages;

  //all task completed
  LogMessages('ALL DONE');
  Button1.Enabled := true;
end;

Did I not initializes something correctly?

Thank you in advance.

John
Logged

Primoz Gabrijelcic

  • Administrator
  • Hero Member
  • *****
  • Posts: 569
    • View Profile
    • Email
Re: Problem with GlobalOmniThreadPool.CountExecuting
« Reply #1 on: January 28, 2012, 07:35:47 AM »

I'm consistently getting

task Loading Inventory - 1 / 17 start
task Loading Parts - 2 / 18 start
0 Inventory records loaded.
0 part records loaded.
ALL DONE

What are you seeing?
Logged

Qmodem

  • Newbie
  • *
  • Posts: 9
    • View Profile
    • Email
Re: Problem with GlobalOmniThreadPool.CountExecuting
« Reply #2 on: January 28, 2012, 08:26:59 PM »

I get the "ALL DONE" first and then everything else the same as you.  I figured that it was possible that my machine is fast enough that the createtask calls were not quit through their setups to the solution I came up with was either to put a Sleep(500) after the second createtask, or

I do a


 while GlobalOmniThreadPool.CountExecuting = 0 do
    Application.ProcessMessages;

And this insures that the tasks are started before then testing to when they are done.

Either one fixed the issue, but I went with the sleep command because I didn't want an error in the tasks to cause the CountExecuting to always be 0 and cause an endless loop.

John
Logged

Primoz Gabrijelcic

  • Administrator
  • Hero Member
  • *****
  • Posts: 569
    • View Profile
    • Email
Re: Problem with GlobalOmniThreadPool.CountExecuting
« Reply #3 on: January 29, 2012, 04:33:55 AM »

Understood. I'll check it up further.
Logged

Primoz Gabrijelcic

  • Administrator
  • Hero Member
  • *****
  • Posts: 569
    • View Profile
    • Email
Re: Problem with GlobalOmniThreadPool.CountExecuting
« Reply #4 on: January 31, 2012, 08:17:45 AM »

This is a feature (which could be also considered a bug) of the thread implementation - Schedule only sends a request to the internal worker thread and does not increment the 'queued' count. I'll fix this.
Logged

Primoz Gabrijelcic

  • Administrator
  • Hero Member
  • *****
  • Posts: 569
    • View Profile
    • Email
Re: Problem with GlobalOmniThreadPool.CountExecuting
« Reply #5 on: January 31, 2012, 08:49:26 AM »

I have committed a fix but it won't change much in your example - due to asynchronous execution and Application.ProcessMessages you may still get '... loaded' messages after 'ALL DONE'.

You can achieve stronger synchronisation by setting up TOmniCounter (or IOmniCounter; implemented in OtlCommon.pas) before starting background tasks. Initialize it to 2 (two background tasks) and then Decrement it in OnTerminate after you have finished everything else. Check if this counter is 0 in your wait loop.

Code: [Select]
var
  CountTasks: IOmniCounter;
begin
  CountTasks := CreateCounter(2);
  ...
  CreateTask(LoadInventoryRecords, 'LoadInventoryRecords')
    .OnMessage(...)
    .OnTerminated(
      procedure(const task : IOmniTaskControl)
      begin
        LogMessages(Format('%d Inventory records loaded.', [0{RS_Inventory.RecordCount}]));
        CountTasks.Decrement;
      end)
    .SetParameter('TaskNumber', 1)
    .Schedule(GlobalOmniThreadPool);

  CreateTask(LoadPartRecords, 'LoadPartRecords')
    .OnMessage(...)
    .OnTerminated(
      procedure(const task : IOmniTaskControl)
      begin
        LogMessages(Format('%d part records loaded.', [0{RS_Part.RecordCount}]));
        CountTasks.Decrement;
      end)
    .SetParameter('TaskNumber', 2)
    .Schedule(GlobalOmniThreadPool);

  //wait all finished
  while CountTasks.Value > 0 do
    Application.ProcessMessages;

  //all task completed
  LogMessages('ALL DONE');
  Button1.Enabled := true;
end;
Logged

Qmodem

  • Newbie
  • *
  • Posts: 9
    • View Profile
    • Email
Re: Problem with GlobalOmniThreadPool.CountExecuting
« Reply #6 on: January 31, 2012, 10:30:22 AM »

Excellent.  I have to admit this is my first adventure into programming with threads.  I am sure I will make good use of your forum while I learn the ins and outs of OTL. 

I am most interested in how I can put this to use in my application that is heavily dependent on SQL Server and ADO.  So I am sure some of my questions will seem simple, but I have to start somewhere. 

Thank you.
Logged
Pages: [1]   Go Up
 
 

Powered by MySQL Powered by PHP Powered by SMF 2.0.2 | SMF © 2006-2009, Simple Machines LLC

Valid XHTML 1.0! Valid CSS! Dilber MC Theme by HarzeM