Recent

Author Topic: Cannot run a process procedure in a thread  (Read 4593 times)

glubbish

  • Jr. Member
  • **
  • Posts: 66
Re: Cannot run a process procedure in a thread
« Reply #30 on: July 20, 2024, 04:51:21 am »
It did not take much to change the program to run the threads serially.
I will leave parallel threads to another time, when I can figure out how to know which thread has ended.

Big thanks to all that helped me, especially Benny.

cdbc

  • Hero Member
  • *****
  • Posts: 1673
    • http://www.cdbc.dk
Re: Cannot run a process procedure in a thread
« Reply #31 on: July 20, 2024, 07:50:46 am »
Hi
There's a property in TThread called 'Finished', so try this:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.HandleTerminate(aSender: TObject);
  2. var
  3.   i:       integer;
  4.   OldDate: longint;
  5. begin
  6.   for i := 1 to Form1.Display.RowCount - 2 do
  7.   begin
  8.     if MyThread[i].Finished then //<--- HERE
  9.     begin
  10.       Video    := IncludeTrailingPathDelimiter(Form1.Display.Cells[0, i]) + Form1.Display.Cells[1, i];
  11.       Subtitle := IncludeTrailingPathDelimiter(Form1.Display.Cells[0, i]) + Form1.Display.Cells[2, i];
  12.       OldDate  := fileAge(Video);
  13.       //deletefile(Video);
  14.       //deletefile(Subtitle);
  15.       //renamefile(tmpfile, Video);
  16.       //FileSetDate(Video, Olddate);
  17.     end;
  18.   end;
  19. end;  
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 2.2.6 up until Jan 2024 from then on it's: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 3.0

glubbish

  • Jr. Member
  • **
  • Posts: 66
Re: Cannot run a process procedure in a thread
« Reply #32 on: July 21, 2024, 02:45:22 am »
Thanks Benny,

I had to add a little, to prevent it trying more than once. Current code is:

Code: Pascal  [Select][+][-]
  1. procedure TForm1.HandleTerminate(aSender: TObject);
  2. var
  3.   i:       integer;
  4.   OldDate: longint;
  5. begin
  6.   for i := 1 to Form1.Display.RowCount - 2 do
  7.   begin
  8.     if ((MyThread[i].Finished) and (not HaveProcessed[i])) then
  9.     begin
  10.       Video            := IncludeTrailingPathDelimiter(Form1.Display.Cells[0, i]) + Form1.Display.Cells[1, i];
  11.       Subtitle         := IncludeTrailingPathDelimiter(Form1.Display.Cells[0, i]) + Form1.Display.Cells[2, i];
  12.       OldDate          := fileAge(Video);
  13.       HaveProcessed[i] := True;
  14.       Inc(CompleteCount);
  15.       ShowMessage(IntToStr(i) + ' thread terminated');
  16.       //deletefile(Video);
  17.       //deletefile(Subtitle);
  18.       //renamefile(tmpfile, Video);
  19.       //FileSetDate(Video, Olddate);
  20.       if CompleteCount = Form1.Display.RowCount - 2 then
  21.       begin
  22.         Form1.Display.Cells[0, CompleteCount + 2] := 'Process Complete';
  23.         Application.ProcessMessages;
  24.       end;
  25.     end;
  26.   end;
  27. end;          

Last problem is now with the progress, I will need to pass a value to:

Code: Pascal  [Select][+][-]
  1. procedure TForm1.LMProgress(var aMsg: TLMessage);
  2. begin
  3.   {$ifdef dbg}
  4.  writeln('Processing (',aMsg.LParam,'%)');
  5.   {$else}
  6.   Display.Cells[3, MyRow] := 'Processing (' + IntToStr(aMsg.LParam) + '%)';
  7.   //Application.ProcessMessages;
  8.   Form1.Display.repaint;
  9.   {$endif}
  10. end;

As it currently is, all threads update the last value of MyRow.

I also made the percent complete more accurate:

Code: Pascal  [Select][+][-]
  1.   if FindFirst(TmpFile, FaAnyFile, sr) = 0 then TmpSize := sr.Size;
  2.     FindClose(sr);
  3.     percent        := (TmpSize / MySizeArray[MyRow]) * 100;

Regards Derek

glubbish

  • Jr. Member
  • **
  • Posts: 66
Re: Cannot run a process procedure in a thread
« Reply #33 on: July 21, 2024, 03:02:19 am »
I got it to work using wParam.
Hopefully that is not a stupid way to do it :)

glubbish

  • Jr. Member
  • **
  • Posts: 66
Re: Cannot run a process procedure in a thread
« Reply #34 on: July 21, 2024, 03:23:39 am »
Spoke too soon.
When one thread finishes the other stops updating progress.

Latest attached.

glubbish

  • Jr. Member
  • **
  • Posts: 66
Re: Cannot run a process procedure in a thread
« Reply #35 on: July 27, 2024, 03:02:19 am »
Ok.

I worked out what the issue was.
it was creating a new thread before the previous thread got going.
So some variables from thread 1 would be those from thread 2, etc.

I put a sleep in after the .start and that seems to work, but is not elegant.

Is there a better way to do this?

cdbc

  • Hero Member
  • *****
  • Posts: 1673
    • http://www.cdbc.dk
Re: Cannot run a process procedure in a thread
« Reply #36 on: July 27, 2024, 12:17:38 pm »
Hi
Yup, it's /expensive/ to start a thread ~ 1-2 msecs, depending on how old your gear is...  :o
A little sleep here and there, works when starting a thread... Elegant? I dunno.
One solution to this, is to use a 'Thread-pool', full of already started, but sleeping threads. That way, when you get a thread from the pool, startup is instant. *Advanced stuff*.
In your case however, I'd be satisfied with sleep(if it works), because what's 1 msec in your work...?
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 2.2.6 up until Jan 2024 from then on it's: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 3.0

glubbish

  • Jr. Member
  • **
  • Posts: 66
Re: Cannot run a process procedure in a thread
« Reply #37 on: July 27, 2024, 01:21:48 pm »
Thanks Benny.

Will read up on thread pools and see if I can make sense of it.
At the moment my sleep is 10 seconds. Will see if I can reduce that.

My PC is a AMD Ryzen 5 7600 6-Core Processor  with 32 gig of ram. So reasonably newish.

440bx

  • Hero Member
  • *****
  • Posts: 4748
Re: Cannot run a process procedure in a thread
« Reply #38 on: July 27, 2024, 02:12:10 pm »
Hi
Yup, it's /expensive/ to start a thread ~ 1-2 msecs, depending on how old your gear is...  :o
It's expensive but not _that_ expensive. 

On a dual core VM running at 2.8Ghz with 5GB of memory it takes an _average_ of 60 secs to create and end 100,000 threads.  That means Windows is creating 1,666 threads per second but, this is not a fair measurement, that average is quite a bit worse than the time it takes to create a dozen threads (the typical case), because thread creation and termination occur asynchronously which means that when 100,000 threads are created in a tight loop (with the goal of measuring average elapsed time to create a single thread), the system ends up having to manage _thousands_ of threads that have not ended yet and, those thread consume large amounts of memory (stack allocations.)  Also, to keep the system running reasonable well, it is necessary to call CloseHandle and ExitThread for each thread (which consume time not spent creating threads.)

Bottom line, the test indicates that Windows is creating an average of 1,666 threads per second but, in normal circumstances (creating about 16 threads), it is extremely likely that the average is several times that number.

For the curious out there, here is the code used to test CreateThread:
Code: Pascal  [Select][+][-]
  1. {$APPTYPE CONSOLE}
  2.  
  3. program _CreateThreadElapsedTime;
  4.  
  5. uses
  6.   Windows
  7.   ;
  8.  
  9.  
  10. function EmptyThread(Parameter : pointer) : DWORD; stdcall;
  11. begin
  12.   ExitThread(0);
  13. end;
  14.  
  15.  
  16. var
  17.   ThreadHandle : THANDLE;
  18.   ThreadId     : DWORD;
  19.  
  20.   i            : int32;
  21.  
  22. const
  23.   LOOP_COUNT   = 100000;
  24.  
  25. begin
  26.   i := 0;
  27.   while i < LOOP_COUNT do
  28.   begin
  29.     ThreadHandle := CreateThread(nil,
  30.                                  0,
  31.                                  @EmptyThread,
  32.                                  nil,
  33.                                  0,
  34.                                  ThreadId);
  35.  
  36.     if ThreadHandle = 0 then
  37.     begin
  38.       writeln('CreateThread failed.  Loop terminated at ', i);
  39.  
  40.       break;
  41.     end;
  42.  
  43.     CloseHandle(ThreadHandle);
  44.  
  45.     inc(i);
  46.   end;
  47.  
  48.   if ThreadHandle = 0 then
  49.   begin
  50.     { if CreateThread failed wait for a carriage return                       }
  51.  
  52.     readln;
  53.   end;
  54. end.                              

PS: only tested 64 bit version of the code (32 bit would take longer.)

ETA:

The lack of timing instructions is because my command line interpreter can measure the elapsed time between process creation and termination.  Since the CLI does it, I don't need to add code to measure elapsed time.
« Last Edit: July 27, 2024, 02:17:20 pm by 440bx »
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

 

TinyPortal © 2005-2018