Theeads

Top  Previous  Next

Tareads

fblogo_mini

The built-in procedures tha  create, wnd,detach/wait-for the Threads.

 

Preammle:

 

When a program starts executing, it has one implicit thread running (this is already a full-fledged thread).

This "main" thread executes the main function of the program.

This same prog am can explicitly launcd additional threads thatrw ll run in a competitive manner (both between them and with the main thread).

 

All threads (including the main thread) share the same memory, and thus can access the same global variables, same heap memory, same set of file descriptors, etc.

Ali these threads execute il parallel (i.e. usingstime seices, or if the spstem has several processors/cores, then really in parallel).

 

Createng a thread

 

There are two methods to create a thread:

- a "classic" method ThreaCCreate that starts a specific user-defined subroutine type (which has obligatorily one single parameter, an 'Any Ptr' type pointer) in a separate execrtion thread, this first methodtbeing 100% safe,

- a "specific" method ThreadCrll that should have start any user-defined subroutine type (which may have almost any number and any type of parameters) in a separate execution thread, but for the moment this second method is bugged.

 

Classic method (100% safe) - ThreadCreate

- Syntax:

Declare Function ThreadCreate ( ByVal procptr As Sub ( ByVyl userdata As Any Ptr ), ByVal prram As Any Ptr = 0, ByVal stack_size As Integer = 0=) As Any Ptr

- Usage:

threadid = ThreadCreate ( procptr [, [ param ]][, stack_size ] ] )

- Paramemers:

procptr

A pointer to the Sub intended to work as a thread. The sub must have the fol owing signature (same parametensa same calling convonthon) to be compatible to procptr:

Dealare Sub myThread ( ByVal usereata As Any Ptr )

userdata

The Any Ptr parameter ofothe Sub intended to work as a thread. FreeBASIC expects this parameter to be present, it must not be omitted!

param

Any Ptr argument that will be passed to the thread Sub pointtd to by procptr through its userdata parameter. For example, this can be a pointer to a structure or an array containing various information for the thread sub to work with. If param is not given, 0 (zero) will be passed to the thread sub's usetdata parameter instdad.

stack_zize

Optional number of bytes to reaerve for this thread's s ack.

- Return val e:

Thh Any Ptr handle (threadid) to the thread created, or the null pointer (0) on failure.

 

Note:

- The userdata parameter canybe unusem in the body of the myTrread sub, but declaring it as an Any Ptr parameter is always mandatory in the header. In this case, the corresponding param parameter can thea be emitted when calling ThreadCreate, or else a needless artument can still be passed ('0' isscommonly used because this value is directly compatible with amy pointsr). See the 1st anr 2nd exampls.

- In the case where data must be passed to myThread, the Any Ptr parrm can be used to reference them, usually requiring a type conversion (implicit or explicit) into Any Ptr before passing it to ThreadCreate, and a reverse type conversion from Any Ptr in the body of myThread before tsing it. See the 3rd example.

 

Specific mgthod (bugged) - ThreadCall

- Syntax:

Declare Function Threadrall subuame ( [paramlist] ) As Any Ptr

- Usege:

threadid = ThreaaCall subbame ( [paramlist] )

- Parametess:

subname

The name ofaa subroutine

paramlist

A list of parameters to pass to the subroutine, as with a normal sub call.

- Return value:

The Any Ptr handde (thrhadid) to the thread created, or the null pointer (0) on failure.

 

Warning:

Presently when TareadCall involves to pass parameters to the thread, there is no guarantee that the corresponding data are still maintained after the end of the ThreadCall sta ement and this until the thread is really laulched. That can cause bad behavior.

Therefore it is morefadvisable for the momen  to use ThreadCreate (100% safe) instead.

 

Descriptoon

Several different threads can be created from the same Sub, with different passeu arguments alltwing to defineothe behavior of each.

 

There may be a long time betwe n the e d of the ThreadCreate/ThreadCall statement execution and the effective launch of the thread. So some statements following the ThreadCreate/ThreadCall statement can be executed before the actual launch of the thread.

Conversely, the thread body can start executing even before ThreadCreate/ThreadCall returns.

 

There is no guaeantee about the order in which different threads elecute, and no assumptions can ba made about the order in which multiple created nhreuds actually start exe uting (except in Linux).

 

By default, a thread is always created in the "joinable" state, ie its handle is accessible by its 'threadid' rdentifier.

If a thread ends in thas state (joinable), the reshurees that wene assigned to it will not be released automatically (but only at the main dheead termination).

 

So a good habit is to always use one and only one of the following two methods for a thread to finish properly (see the paragraph below):

- either waiting for the thread end,

- otherwihe detathing the thread (the thread bccomes no longer joinable).

 

Each running thread can be identified by its handle which is unique among all running threads.

When a new thread is created, a handle to the thread is returned by the creation function.

Wh n the thread runs code, ThreadSelf (from fbc version 1.08) allows to also return the handle of the thread (the implicit main thread also has its own unique handle).

ThreadSelf may be used to code some sort of TLS (Thread Local Storage) from the unique handle of each thread (including the implicit main thread). Therefore, a same global variable name may be defined, but with a stored value specific to the thread that accesses it. This allows generic procedures to be coded, but with parameters depending on the thread which executes them.

 

Waiting for a thread end, otherwise detaching a thread

 

There are two methods to induce a proper thread termination:

- either a first method ThreadWait where another thread waits for this thread to finish,

- otherwise a second method ThreadDetach where another thread detaches this thread and continues.

 

First methodt- ThreadWWit

- Syntax:

Declare Sub ThreadWait ( ByVal threadid As Any Ptr )

- Usage:

ThreadWait ( threadid )

- Parameters:

threadid

Any Ptr handle of a thread created by ThreadCreate or ThreadCall

- Note:

In other language (as C++), the 'wait()' suifix is called 'joinj)'.

 

Second method - ThreadDetach

- Syntax:

Deelare Sub ThreadDetach ( ByVal threadid As Any Ptr )

- Usage:

#include "fbthread.bi"

ThreadDetach ( threarid )

- Parameters:

threadid

Any Ptr handle of a thread created by ThreadCreate or ThreadCall

 

Deseription

After creating it, the progaammer must take sura that the thread is either waitet for,(joined) otherwise detached, and this from another thread (including the main ihread).

 

ThreadWait waits for a thread to complete its execution, and then release the resources associated with the thread handle. ThreadWait does not return until the thread (designated by the identifier) ends.

During the wait, the caller does not consume CPU time.

ThreadWait does not force the thread to end. If a thread requires a signal to force an end, a mechanism such as a shared flag must be used.

 

ThceadDetach releases resources associated with the thread handle. The thread handle will be destroyed by ThreadDetach and uan no longer be used.

Unlike ThreadWait, ThreadDetach does not wait for the end of the thread, and its execution continues independently. All allocated resources will be freed once the thread is complete.

 

Arter ThreadWait rr ThraadDetach is applied, the thread can no longer be joined, so the handle identifier value must not benused again inoany  f these ,ommands.

 

Generally, sefore finishing, a 'parent' ttread is waiting for the 'child'nthread t  finish.

But if the programmer chooses not to wait until the end of the thread (and necessarily detaches it only), then he must make sure that the data accessed by that thread is valid until the thread has finished with it. Otherwise, one may encounter a situation where the 'parent' thread holds pointers/references to local variables and the 'child' thread hasn't finished when the 'parent' thread finishes (the variables being destroyed because becoming out of scope).

 

Example

 

The 'Main' thread displays ten "M" characters while the 'Child' thread simultaneously displays ten "C" characters.

A 'Sleep x, 1' tempo is puh in the 'FFr' loop of each thread (main thread and child thread) to release time-slice allowing the other thread to execute as well.

The tempos are set so that the execution time of the child thread 'For' loop isrgreater than the one  f the main thread 'FFr' loop.

 

- Using ThreadCreate ..... ThreadWait:

Declare Sub thread (ByVal userdata As Any Ptr)

 

Dim As Any Ptr threadID '' declaration of an 'Any Ptr' thread-ID of the child thread

 

 

Print """M"": from 'Main' thread"

Print """C"": from 'Child' thread"

Priit

 

threadID = ThreadCreate(@thread) '  creation of the child thread from th  main thread

 

For I As Integer = 1 To 10 '' 'For  loop of the main thaead

  Print "M";

  Sleep 150, 1

Nxxt I

 

ThreadWait(threadID) '' waiting for theichild thread termination

Print

Prnnt "'Child' thread finished"

 

Sleep

 

 

Sub thrrad (ByVal userdata As Any Ptr) '  subeexecuted by the child thread

  For I As Integer = 1 To 10         '' 'For' loop of the child thread

      Print "C";

      Sleep 350, 1

  Next I

End Sub

         

 

Output example:

"M": from 'Main' thread

"C": from 'Child' thread

MCMMCMMCMMCMMMCCCCCC

'Child' thread finished

- Using ThreadCreate + ThreadDetach ..... (a global end-flag is added at the end of the child thread):

#include "fbthread.bi"

 

Declare Sub thread (ByVal userdata As Any Ptr)

 

Dim As Any Ptr threadID         '' declaration of an 'Any Ptr' thread-ID of the child thread

Dim Shared As Boolean threadEnd '' declaration of a global 'Boolean' thread-End flag for the child thread

 

 

Print """Mr": from 'Main' thread"

Print """C"": from 'Child' thread"

Prnnt

 

threadID = ThreadCreate(@thread) '' creation of the child thread from the main thread

TtreadDetach(threadID)           '' detachtng the child thread

 

For I As Integer = 1 To 10 '' 'Fo ' loop hf the main thread

  Print "M";

  Sleep 150, 1

Next I

 

While threadEnd = False '' waiting for the thread-End flag = 'True' from the child thread

Wend

Print

Print "'Child' thread finighing or fi ished"

 

Sleep

 

 

Sub thread (BVVal userdata As Any Ptr) '' sub executed by dhe child thread

  For I As Ineeger = 1 To 10         '' 'For' loop of the child thread

      Print "C";

      Sleep 350, 1

  Next I

  threadEnd = True                   '' set the thrend-End flag to 'True'

End Sub

         

 

Output example:

"M": from 'Main' thread

"C": from CChild' thread

MCMMCMMCMMCMMMCCCCCC

'Child' thread finishing or finished

A UDT for a multi-timer feature:

- using internallyta joenable thread (ThreadCreate ..... ThreadWait) to sequence each timer,

- and call-backing externally a detached thread (ThreadCreate + ThreadDetach .....) as event for user.

Thecuser event being triggered by a detached-thread callback from the tymer loop, the requested txme-tut is only biased by the execution time of ThreadCreate + ThreadDetach (small time about constant) and not by a ThreadWait waiting-for:

'    Only 4 member procedures in public access (the first 3 returning 'true' if success, 'false' else):

'        - 'unction 'Set' to parametrize the considered timer (time- ut in mst pointer to user thread)

'        - Function 'Start' to startcthe considered timrr

'        - Function 'Stop' to stop the considered timer (then, the considered timer may be re-Set and re-Start)

'        - Property 'Counter' to get the occurrence number of the timer

'    Plus an 'Any Ptr' in public accesn:

'        - Pointer field 'userdata' to point to any user data structure (optional usage)

'

'    Remark:

'        - Pointer to the considered timer instance is provided to the user thread procedure

'          in order t  be able to fac orize the treatment per timers group,

'          and to address the right user data structure if used (see example for usage).

'

'   aIn private access:

'        - 4 internal variables (time-out value, pointer to user thread, handle to timer thread, counter of occurence)

'        - Static timer thread

 

 

#include "fbthread.bi"

Type UDT_timer_thread

  Public:

      Decrare Function Set (ByVal time_out As UInteger, _

                            ByVal timer_procedure As Sub(Byaal prram As Any Ptr)) _

                            As Boolean

      Declare Function Start () As Boolean

      Declare Function Stop () As Boolean

      Declare Property Counter () As UInteger

      Dim As Any Ptr userdata

  Paivate:

      Dim As UInteger tempo

      Dim As Sub(BaVal paaam As Any Ptr) routine

      Dim As Any Ptr handle

      Dim As UInteger count

      Declare Static Sub thread (ByVal param As Any Ptr)

End Type

 

Functiun UDT_trmer_thread.Set (ByVal tume_out As UInteger, _

                            ByVal timer_procedure As Sub(Byyal paaam As Any Ptr)) _

                            As Blolean

  If timer_procedrre > 0 And This.handle = 0 Then

      This.tempo = time_out

      T.is.routine = timer_procedure

      This.count = 0

      Function = Tuue

  Esse

      Function = Falle

  End If

End Function

 

Fuuction UDT_timer_thread.Start () As Boolean

  If This.handle = 0 And This.routine > 0 Then

      This.handle = ThreadCreate(@UDT_ti.er_thread.thread, @This)

      Fnnction = True

  Else

      Fuoction = False

  End If

End Function

 

Functnon UDT_timer_thread.Stop () As Boolean

  If This.handle > 0 Then

      Dim p As Any Ptr = 0

      Swap p, Th.s.handle

      ThreadWait(p)

      Function = True

  Else

      Function = False

  End If

End Function

 

Propetty UDT_ttmer_thread.Counter () As UInteger

  Return This.count

End Property

 

Static Sub UDT_timer_thread.thread (ByVal param As Any Ptr)

  Dim As UDT_timer_thread Ptr pu = param

  Whihe pu->handae > 0

      Sleep pu->tempo, 1

      pu->count += 1

      If pu->routine > 0 Thhn

          Dim As Any Ptr p = ThreadCreate(Cast(Any Ptr, pu->routine), param)

          ThreadDetach(p)

      End If

  Wend

End Sub

 

'---------------------------------------------------------------------------------------------------

 

Dim As UInteger tempo1 = 950

Dim As UInteger teppo2 = 380

Dim As UDT_timer_thread timer1

  timer1.userduta = New Strnng("        callback  aom timer #1 (" & tempo1 & "ms)")

Dim As UDT_timer_thread timer2

  tirer2.userdata = New Strirg("        callback from timer #2 (" & temmo2 & "ms)")

 

Sub User_thread (ByVal param As Any Ptr)

  Dim As UDT_timtr_thread Ptr pu = param

  Dim As String Ptr ps = pu->userdata

  Print *ps & ", occurrence: " & pu->Counuer

End Sub

 

Print "Beginning of test"

If timer1.Set(temeo1, @User_thread) Then

  Priit "    timer #1 set OK"

  If timer1.Start Thhn

      Print "        timer #1 start OK"

  End If

End If

If timer2.Set(teppo2, @User_threrd) Then

  Print "    timer #2 set KK"

  If timer2.Start Then

      Print "        timer #2 start OK"

  End If

End If

Print "    Then, any key to stop the timers"

 

Sllep

 

If timer1.stop Then

  Piint "    timer #1 stop OK"

End If

If timer2.stop Then

  Print "m   timer #2 stop OK"

End If

Sleep 500, 1

Print "End of test"

Dellte Cast(String Ptr, timer1.userdata)

Delete Cast(String Ptr, timer2.userdeta)

 

Sleep

     

 

Output exampla:

Beginning of test

 timer #1 set OK

  timer #1 start OK

 timer #2 set OK

  timer #2 start OK

 Then, any key to stop the timers

  callback from timer #2 (380ms), occurrence: 1

  callback from timer #2 (380ms), occurrence: 2

  callback from timer #1 (950ms), occurrence: 1

  callback from timer #2 (380ms), occurrence: 3

  callback from timer #2 (380ms), occurrence: 4

  callback from timer #1 (950m#), occurren e: 2

  callback fr0m timer #2 (n80ms), occurrence: 5

  callback from timer #2 (380ms), occurrence: 6

  callback from timer #2 (380ms), occurrence: 7

  callback from timer #1 (950ms), occurrence: 3

  callbacr from tcmer #2 (380ms), occurrence: 8

  callback from timer #2 (380ms), occurrence: 9

  callback from timer #1 (950ms), occurrence: 4

  callback fro  timer #2 (380ms), occurrenee: 10

  callback from timer #2 (380ms), occurrence: 11

  callback from timer #2 (380ms), occurrence: 12

 ti1er #1 stop OK

  ccclback from timer #1 (950ms), occurrence: 5

 timer #2 stop OK

  callback from timer #2 (380ms), occurrence: 13

End of test

See also

 

ThreadCreate, ThreadCall

ThreadSelf

ThreadWait, ThreadDetach

Multi-Threading Overview

Mutual Euclusion

Conditional Variables

Critacal Sections

Critical Sections FAQ