Recent

Author Topic: tthread code tthread.create/tthread,execute/constructor  (Read 1965 times)

toby

  • Jr. Member
  • **
  • Posts: 66
tthread code tthread.create/tthread,execute/constructor
« on: July 17, 2019, 07:09:03 pm »
Hi

i'm exploring what is really going on with the threading code i have and would love to understand it

there really is no good explanation on the net for what i have here using tthread (any references would be welcomed)

i got the basic coding structure from the fpc sources -> /packages/fcl-base/examples/threads.pp

i have studied (studied := read many many times) especially the tthread class section and i can't get the understanding i'd like from it

-

my basic tthread program structure is

program ft2;

type tmthread = class(tthread)
       ch : longint;
       procedure execute; override;
       constructor create(c : longint);
       end;

var t : tmthread;

procedure wf(p : longint);
begin
..
sysutils.executeprocess(curl, ....
..
end;

procedure tmthread.execute;
begin
wf(ch);
end;

constructor tmthread.create(c : longint);
begin
ch := c;
inherited create(false);
end;


begin
for i := 1 to 750 do
  begin
  t := tmthread.create(i);
  end;

sleep(1000);

end.

-

my goal is to understand what happens when t := tmthread.create(i);   is called

why the tmthread,create doesn't need to be passed 'ch'
my understanding : tmthread.execute is in the tmthread class so it gets all of tmthread class variables?

why the need for separate tmthread.create and tmthread.execute 'objects'? (procedure tmthread.create and consturctire tmthread.create ?)
can they be combined into one constructor/procedure?
and what the inherited create(false) really means.

thank you

--

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 675
    • Lebeau Software
Re: tthread code tthread.create/tthread,execute/constructor
« Reply #1 on: July 17, 2019, 08:28:52 pm »
my goal is to understand what happens when t := tmthread.create(i);   is called

TThread is just a class like any other.  Create() is its constructor (just like with most other classes).  You have to create an object instance of the class in memory before you can use it.  No different than any other class.

why the tmthread,create doesn't need to be passed 'ch'
my understanding : tmthread.execute is in the tmthread class so it gets all of tmthread class variables?

Yes.  You pass a value to your class's constructor, which stores that value in a member variable, which is accessible to all methods of the class.  No different than how any other class works.

why the need for separate tmthread.create and tmthread.execute 'objects'? (procedure tmthread.create and consturctire tmthread.create ?)

They are not 'objects'.  Create() is a constructor, Execute() is a procedure method.

Where TThread differs from other classes is that its Execute() method is always called in the context of a dedicated worker thread that is different than the thread that calls the constructor.

You do know what is a thread is, don't you?  If not, you need to go study up on that.  Any decent programing book should cover that topic.

can they be combined into one constructor/procedure?

No.  Create() creates the class object in memory, and then creates a worker thread that calls Execute() on that object.

and what the inherited create(false) really means.

The base TThread constructor has a boolean parameter named CreateSuspended:

Code: [Select]
public constructor TThread.Create(
  CreateSuspended: Boolean;
  const StackSize: SizeUInt = DefaultStackSize
);

Your derived class's constructor is calling the base class's constructor, telling it to create its worker thread in a non-suspended state, ie the thread starts running as soon as it is created (well, after the constructor exits, anyway).

Sometimes, you may need to set CreateSuspended to True instead, to delay the thread from running until you explicitly call TThread.Resume() or TThread.Start().  Typically, you do this when you need to perform additional initializations after the constructor exits on things that the thread needs to access.  Or, if you want to create a pool of reusable threads that are resumed and suspended on-demand.
« Last Edit: July 17, 2019, 08:30:25 pm by Remy Lebeau »
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

Peter H

  • Jr. Member
  • **
  • Posts: 57
Re: tthread code tthread.create/tthread,execute/constructor
« Reply #2 on: July 17, 2019, 09:19:01 pm »
I dont know, if this program will run. I doubt it.

It creates 750 threads in the adress-space of your process.
The default stacksize for threads -in windows- is 4 megabytes.
So this program would need 3 gigabytes of adress-space, all in your process.
This might be possible in a 64 bit process, but probably not in 32  bit.

Note: It will need this adress-space, but not the physical memory.
The memory needed ist only the memory that is actually touched (used). (If you have an OS with paged memory)

Better start with something simpler, e.g. "multithreadingexample1" from the examples in lazarus.
You can then set a breakpoint at create(), start the program and then browse the sourcecode in lazarus.
(It is a little bit difficult, because a lot of sourcecode sits in include files)

All the best.
« Last Edit: July 17, 2019, 09:21:31 pm by Peter H »

toby

  • Jr. Member
  • **
  • Posts: 66
Re: tthread code tthread.create/tthread,execute/constructor
« Reply #3 on: July 17, 2019, 11:21:16 pm »
Remy Lebeau

thank you - just what i was needing

are t.terminate; and t.destroy not needed in my case since the threads end on their own in wf() which ends after loop is done?

at the end to keep program alive until all threads are finish - what do you use?

is something like this used and if so should i increase or remove the sleep?

repeat
  sleep(10);  <- 
until t.finished;

---

Peter H

yes indeed it runs - 750 is close to the maximum number of threads that can be created on my computer (767-780 are the max that can created) and .... yes i have 3GB of ram on my machine

in the example multithreainexample1 (which compiles and runs on my machine btw)

the /usr/local/lazarus/examples/multithreading/mainunit.pas
has constructor Create(CreateSuspended: boolean);   

mine is   constructor tmthread.create(c : longint);   how come i don't have any problem passing the parameter c which is the loop/thread number?



lucamar

  • Hero Member
  • *****
  • Posts: 2085
Re: tthread code tthread.create/tthread,execute/constructor
« Reply #4 on: July 18, 2019, 01:45:08 am »
in the example multithreainexample1 (which compiles and runs on my machine btw)

the /usr/local/lazarus/examples/multithreading/mainunit.pas
has constructor Create(CreateSuspended: boolean);   

mine is   constructor tmthread.create(c : longint);   how come i don't have any problem passing the parameter c which is the loop/thread number?

You're creating a new different constructor. Aren't you getting any warnings/errors about your constructor hiding the inherited or about lacking "overload"? (Depends on the $mode directive, if any)
Turbo Pascal 3 CP/M - Amstrad PCW 8256 (512 KB !!!) :P
Lazarus 2.0.2/2.0.4  - FPC 3.0.4 on:
(K|L)Ubuntu 12..16, Windows XP SP3, various DOSes.

toby

  • Jr. Member
  • **
  • Posts: 66
Re: tthread code tthread.create/tthread,execute/constructor
« Reply #5 on: July 18, 2019, 02:24:18 am »
Hi lucamar

so constructor line is like a procedure line - i thought it was a static thing

my compile directives are    -S2ghc   (gotta make sure my gotos work) in /etc/fpc.cfg

no warning/errors and i have -vh on fpc command line also for this project (and testing out -XX for first time on this project on the fpc command line too)

(-XX knocks the size of my lazarus projects in half)

lucamar

  • Hero Member
  • *****
  • Posts: 2085
Re: tthread code tthread.create/tthread,execute/constructor
« Reply #6 on: July 18, 2019, 08:13:25 am »
so constructor line is like a procedure line - i thought it was a static thing

It is basicaly a normal method with the special characteristic that when called as a class method, ie. when doing:
Code: Pascal  [Select]
  1. AnObject := TClass.Create(whatever);
it does some magic behind scenes to create the object instance: allocates memory, builds the VMT if needed, etc. If called as a normal mehod it just executes the code (or fails if the instance doesn't exists) as a normal method would.

Since your code works, it's obvious that the "overload" keyword is not needed. I don't remember for sure but it may be because you're using objfpc mode (-S2, equivalent to -Mobjfpc). It's probably somewhere in the manuals.
Turbo Pascal 3 CP/M - Amstrad PCW 8256 (512 KB !!!) :P
Lazarus 2.0.2/2.0.4  - FPC 3.0.4 on:
(K|L)Ubuntu 12..16, Windows XP SP3, various DOSes.

PascalDragon

  • Hero Member
  • *****
  • Posts: 674
  • Compiler Developer
Re: tthread code tthread.create/tthread,execute/constructor
« Reply #7 on: July 18, 2019, 09:22:34 am »
are t.terminate; and t.destroy not needed in my case since the threads end on their own in wf() which ends after loop is done?

at the end to keep program alive until all threads are finish - what do you use?

Don't call Destroy directly, use Free instead as the later will correctly handle the case if the instance variable is Nil.

You need to use Terminate if you want to stop a thread from the outside though your thread's Execute method needs to check for Terminated and abort cooperatively.

The way you wrote it you need to free all thread instances you created so you need to store them in an array or list so that you can call Free on them once they are Finished. You can also use TThread.WaitFor to wait on each until its done with its execution.

Alternatively you can set FreeOnTerminate to True (e.g. inside your constructor before you call the inherited one). In that case however you must not touch the thread instance from outside as it could have been freed already leading to an access violation (yes, there are specific cases where this works, but let's keep to the general case). Thus you need to use some alternate way to tell the main program that all threads finished.

toby

  • Jr. Member
  • **
  • Posts: 66
Re: tthread code tthread.create/tthread,execute/constructor
« Reply #8 on: July 18, 2019, 03:40:32 pm »
PascalDragon

this works - if i have a real delay finishing the wf thread then the threads get totally unique threadid but if there is no delay in it finishing then the code seems to reuse threadid in alternate threads (the system tries to conserve resources?)

constructor tmthread.create(c : longint);
begin
ch := c;
inherited create(false);
freeonterminate := true;     <---- added
end;

aren't all threads freed automatically when the main program ends?

---

to keep program alive until all threads are finish - what do you use?

i'm using something like this used and if so should i increase or remove the sleep?

repeat
  sleep(10);  <-
until t.finished;

---

lucamar

https://www.freepascal.org/docs-html/ref/refsu79.html

https://wiki.freepascal.org/Compiler-generated_data_and_data_structures

probably best for me to just appreciate what goes on behind the scenes and be glad it works

lucamar

  • Hero Member
  • *****
  • Posts: 2085
Re: tthread code tthread.create/tthread,execute/constructor
« Reply #9 on: July 18, 2019, 04:27:34 pm »
Quote
probably best for me to just appreciate what goes on behind the scenes and be glad it works
Yeah, I'm like that too: Take some quite time to read the docs and get at least a general view, be glad it works ... and then thank the gods I'm not the one that has to make it work! :D
Turbo Pascal 3 CP/M - Amstrad PCW 8256 (512 KB !!!) :P
Lazarus 2.0.2/2.0.4  - FPC 3.0.4 on:
(K|L)Ubuntu 12..16, Windows XP SP3, various DOSes.

toby

  • Jr. Member
  • **
  • Posts: 66
Re: tthread code tthread.create/tthread,execute/constructor
« Reply #10 on: July 18, 2019, 04:53:28 pm »

am i creating potential problem by not using private, protectd, public here?
i don't create units usually so don't know if they are really needed here or not

type tmthread = class(tthread)
//   private
       ch : longint;
//   protected
       procedure execute; override;
//   public
       constructor create(c : longint);
       end;

---

Quote
probably best for me to just appreciate what goes on behind the scenes and be glad it works
Yeah, I'm like that too: Take some quite time to read the docs and get at least a general view, be glad it works ... and then thank the gods I'm not the one that has to make it work! :D

well the good thing is that almost all the behind the scenes magic code is written in fpc so it looks familiar (and not scary)
but i do see something called assembler every once in a while though - <shudder>

-

did you get your apollo guidance computer ?
can you believe they are still trying to get people to believe they sent a rocket device to that thing that moves in the sky and landed on it

http://www.ibiblio.org/apollo/download.html

---

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 675
    • Lebeau Software
Re: tthread code tthread.create/tthread,execute/constructor
« Reply #11 on: July 19, 2019, 12:27:10 am »
The default stacksize for threads -in windows- is 4 megabytes.

Actually, it is dependent on compiler settings, not the OS.  The PE header of an executable specifies the stack size.  That value gets set to whatever you configure your compiler to generate.  But an application's code can override that size at runtime by calling the Win32 CreateThread() or the RTL's BeginThread() directly.
« Last Edit: July 19, 2019, 12:29:14 am by Remy Lebeau »
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

PascalDragon

  • Hero Member
  • *****
  • Posts: 674
  • Compiler Developer
Re: tthread code tthread.create/tthread,execute/constructor
« Reply #12 on: July 19, 2019, 09:25:29 am »
this works - if i have a real delay finishing the wf thread then the threads get totally unique threadid but if there is no delay in it finishing then the code seems to reuse threadid in alternate threads (the system tries to conserve resources?)
It totally depends on the operating system whether it reuses thread IDs, but it is definitely free to do so, so you shouldn't rely on the thread ID if you create many short lived threads.

aren't all threads freed automatically when the main program ends?
It depends on the operating system whether the program ends when the main thread returns or only ends if all threads terminated. Also in your example it might not matter, but in more complex cases you might want the threads to finish gracefully (maybe one is currently writing in a file / to a database?).


am i creating potential problem by not using private, protectd, public here?
i don't create units usually so don't know if they are really needed here or not

type tmthread = class(tthread)
//   private
       ch : longint;
//   protected
       procedure execute; override;
//   public
       constructor create(c : longint);
       end;

It's not a problem, but it's cleaner to correctly encapsulate fields, methods, etc.

The default stacksize for threads -in windows- is 4 megabytes.

Actually, it is dependent on compiler settings, not the OS.  The PE header of an executable specifies the stack size.  That value gets set to whatever you configure your compiler to generate.  But an application's code can override that size at runtime by calling the Win32 CreateThread() or the RTL's BeginThread() directly.
It is also OS dependant. For example on Unix the information is not retrieved from the executable, but the environment. See the man page for pthread_create (Notes section).

toby

  • Jr. Member
  • **
  • Posts: 66
Re: tthread code tthread.create/tthread,execute/constructor
« Reply #13 on: July 19, 2019, 03:57:49 pm »

aren't all threads freed automatically when the main program ends?
It depends on the operating system whether the program ends when the main thread returns or only ends if all threads terminated. Also in your example it might not matter, but in more complex cases you might want the threads to finish gracefully (maybe one is currently writing in a file / to a database?).

my system is on linux

i had to use something to keep thØe main thread open until all the threads finished

i started with a readln; then used  a sleep(2000); and then found property t.finished to keep the main program open until the threads finished with

repeat
writeln('1 t.finished : ', t.finished);      <- false
//sleep(10);
  sleep(500);
until t.finished;
writeln('2 t.finished : ', t.finished);      <- true

i guess this question is more a programming question of understanding exactly what sleep does and how it really works

what interval sleep interval shouls i use in the repeat/until to not waste cpu time needlessly

the sleep(10) gives perfect timing to end when the last thread is finished and the sleep(500) does also most of the time

but the sleep(10) is obviously running 50 times more then the sleep(500) - but i guess it depends on what the underlying 'crontab'
like interupt structure is doing.

lucamar

  • Hero Member
  • *****
  • Posts: 2085
Re: tthread code tthread.create/tthread,execute/constructor
« Reply #14 on: July 19, 2019, 05:34:49 pm »
Very basically, sleep(X) tells the system to suspend the process for at least the next X milliseconds. When X is 0 it means the process just relinquishes the rest of its currently alloted time-slice.

How sleep() is implemented is very platform-dependent. In Linux, IIRC, it's implemented with a call to nanosleep()

Regarding your code: you don't need to loop waiting for the thrread to terminate; you can simply call Thread.WaitFor and it won't return until it ends. Of course that will "lock" your program, but so does what you're doing now, doesn't it? So instead try this:
Code: Pascal  [Select]
  1. writeln('1 t.finished : ', t.finished);      <- false?
  2. if not t:Finished then t.WaitFor;
  3. writeln('2 t.finished : ', t.finished);      <- true?
« Last Edit: July 19, 2019, 05:44:01 pm by lucamar »
Turbo Pascal 3 CP/M - Amstrad PCW 8256 (512 KB !!!) :P
Lazarus 2.0.2/2.0.4  - FPC 3.0.4 on:
(K|L)Ubuntu 12..16, Windows XP SP3, various DOSes.