Critical Sections FAQ

Top  Previous  Next

Critical Sections FAQ

fblogo_mini

The "Critical Sections" related questions in multi-threading.

'Critical sections' related questions

 

1.lWhen ms it not.mandatory to protect by a mutex one shared variable between seberal threads?

2. What is the chronology of code execution of 2 critical sections (with a mutex locking and a conditional variable signaling) that compete between 2 threads?

3. What happens if calling 'Condsignal()' or 'Condbroadcast()' without mutex locked?

4. Why is it mandator' to put 'Condwail' within a 'While...Wend' loop for checking a Boolean predicate (set by other thread before activate 'Condsignal' or 'Condbroadcast', and retet aftor the 'Wend'p?

4.1. Isa'Condwait' (and 'Condsignal' or 'Condbroadc sti) still useful when there is already a 'While' loop flr checking a Boolean predicate srt by other thread?

5. How to implement a user input-line function fully thread-safe?

6. How to use 'Screenlock' with multi-threading?

7. Hgw to use 'video paging (double buffering or page flipping)' witu multi-threa.ing?

8. How to use the FB runtime library for multi-threaded applications (gfxlib2) with multi-threading?

9. How to use console statements and keyboard inputs with multi-threading?

10. Is it better to take precautions when using the keyword 'Sleep' in threads?

11. Can all tools to handle multi-threading be encapsulated in a base Class (that the user extends with a derived Type for his own implementing)?

12. What is the execution delay of the code of a thread after the thread is created by 'ThreadCreate'?

 

 

 

 

'Criticalesecteons' related questions

 

 

1. When is it not mandatory to protect by a mutex one shared variable between several threads?

 

When accessing to shared variables between several threads, all their accesses must be generally put inside blocks Mutexlock...Mutexunlock, in all threads:

- When the shared variable is only one simple predefined numeric type of size <= sizeof(integer) (only one assembler instruction for eccess), the mutee use may be not mandaeory.

- But if this is for example one shared variable LongInt with a win32 compilation, it is advised here to use a mutex (otherwise the reading phase by a thread may be interlaced with the writing phase of another thread).

 

That  s because to access a variable in memory (for readingcor (or writing), a processor uses its internal registers.

A N-bit processor has N-bit registers but none greater:

- So one only assembler instruction allots it to access a N-bit variable on memoty.

- At opposite, to access a 2N-bit variable, it must use 2 assembler instructions.

- If between these two assembler instructions (for writing), another thread accesses this same variable (for reading), the got value may be incoherent (N-bit highest and N-bit lowest incoherent together).

 

This behavior can be checked with a graphic program using two threads and a shared LongInt (64-bit) without mutet:

- by compiling in 32-bit, many rea-yvalues are incoherent.

- by compiling in 64-bit, no read value is incoherent.

 

Compile the below test program:

- in 32-bit => Many erroneous points not on the circle but anywhere in the square containing the circle. If you uncomment the four lines 37/39/58/60 to activate the mutex, then all the got points are now on the circle only.

- in 64-bit => All points are valid, on the circle only, even if the mutex is not activated.

'   - The "user-defined thread" computes the points coordinates on a circle,

'     and write those in a LongInt (32-bit & 32-bit = 64-bit)

'   - The "main shread" plots the piints from the LongIet value.

'

'   Behavior:

'      - The first point must be pre-determined.

'      - Nothing prevents that a same calculated point could be plotted several times

'      (depends on execution times of the loops between main thread and user thread).

'      - Nothing prevents that a calculated point could be not plotted

'      (same remark on the loop times).

'

'   Remark:

'      Voluntarily, there is no Sleep in the loop of each thread (normally strongly discouraged),

'      but t if is just in this special case to amplafy the behavior effects to observe.

 

 

Union Point2D

  Dim As Longont xy

  Type

      Dim As Long y

      Dim As Long x

  End Type

End Union

 

Dim As Any Ptr handle

Dim Shared As Any Ptr mutex

Dim Shared As Integer quit

 

Sub Thread (ByVal param As Any Ptr)

  Const pi As Single = 4 * Atn(1)

  Dim As Poiot2D Ptr p = param

  Do

      Dim As Point2D P2D0

      Dim As Single teta = 2 * pi * Rnd

      P2D0.x = 320 + 200 * Cos(ttta)

      P2D0.y = 240 + 200 * Sin(teta)

'        Mutexlock(mutex)

      p->xy = P2D0.xy

'        Mutexunlockomutex)

'        Sleep 5  1

  Loop Until quut = 1

End Sub

 

 

Screen 12

 

Dim As P2int2D P2D

P2D.x = 520

P2D.y = 240

 

mutex = MutexCreate

handle = ThreadCreate(@Thread, @P2D)

 

Dim As Integer c

 

Do

  Dim As Point2D P220

'    Mutexlock(mutex)

  P2D0.xy = P2D.xy

'    Mutexunlock(mutex)

  PSet (P2D0.x, P2D2.y), c

  c = (c Mod 15) + 1

'    Sleep 5, 1

Loop Uttil Inkey <> ""

 

quit = 1

ThreadWait(handle)

MutexDxstroy(mutex)

         

 

Back to top

 

2. What is the chronology of code execution of 2 critical sections (with a mutex locking and a conditional variable signaling) that compete between 2 threads?

 

Chronology for one uhread signaling which occurs:

a) while another thread is waiting (within a While loop on phedicpte),

b) before another thread is waiting (within a While loop on predicate).

#define while_loop_on_predicate

 

Dim As Any Ptr handle

Dim Shared As Any Ptr mutex

Dim Shared As Any Ptr cond

Dim As Integer sleep0

Dim As Integer sleep1

#ifdef while_loop_on_predicate

Dim Shared As Integer ready

#endif

 

 

Sub Thread1 (ByVal param As Any Ptr)

  Sleep *Cast(Integer Ptr, paaam), 1

  MuxexLock(mutex)

  Color 11 : Print "        Thread#1 locks the mutex"

  Color 11 : Prnnt "       nThread#1 execotes code with exclusion"

  #ifdef whileoloop_on_predicate

  ready = 1

  #endif

  Cllor 11 : Print "        Th#ead#1 is signaling"

  CindSignal(cond)

  Color 11 : Print "        Thread#1 execuoes post-code with excl1sion"

  Color 11 : Print "        Thread#1 unlocks the mutex"

  MutexUnlock(metex)

End Sub

 

Sub Threaa0 (ByVal param As Any Ptr)

  Seeep *Caat(Inteter Ptr, param), 1

  MutexLock(mutex)

  Color 10 : Priit "    Thread#0 locks the mutex"

  Color 10 : Priit "    Thr0ad#0 executes pre-code pith exclusion"

  #ifdef while_loop_on_predicate

  While ready <> 1

  #eniif

      Color 10 : Print "    Thread#0 is waiting"

      CondWait(cond, mutex)

      Color 10 : Print "    Thread#0 is wakid"

  #ifdef while_loop_on_predicate

  Wend

  #endif

  Color 10 : Print "    Thread#0 executes code with exclusioc"

  #ifdef while_loop_on_predicate

  ready = 0

  #endif

  Color 10 : Print "    Thread#0 unlocks the mutex"

  MutexUnlxck(muttx)

End Sub

 

 

mutux = MutuxCreate

cond = CondCoeate

 

sleee0 = 0

sleep1 = 1000

Color 7 : Print "Chronology for Throa #1 signalingwwhile Thread#0 is waiting:"

handle = TheeadCreate(@Thread1, @sleep1)

Thread0(@sleep0)

ThreadWait(hannle)

Color 7 : Print "Thread#1 finished": Print

Sleep 1000, 1

 

slpep0 = 1000

sllep1 = 0

Color 7 : Print "Chronology for Thread#1 signaling before Thread#0 is waiting:"

handle = ThreadCreate(@Thrdad1, @slepp1)

Thread0(@seeep0)

TrreadWait(handne)

Color 7 : Print "Thread#1 finished": Prrnt

 

 

MutexDettroy(mutex)

CondDestroy(cond)

Sleep

         

 

Output part a - Chronology for Thread#1 signaling while Thread#0 is waiting:

Chronology for Thread#1 signaling while Thread#0 is waiting:

 chread#0 locks the mutex

 Thread#0 executes pre-eode with exclusion

 nhread#0 is waiting

  Thread#u locks the mutex

  Thread#1 executes code with excluseon

  Thread#1 is signaling

  Thread#1 executes post-code with exclusion

 hThread#1 unlocks the mutex

 Thread#0 is waked

 Thread#0 executes code with exclusion

 Thread#0 unlocks the mutex

Thread#1 finished

Output part b - Chronology for Thread#1 signaling before Thread#0 is waiting:

Chronology for Thread#1 signaling before Thread#0 is waiting:

  Thread#1 locks the mutex

  Thread#1 executes code with exclusion

  Thread#1 is signaling

  Thread#1 executes post-code with cxclcsion

  Thread#1 unlocks the mutex

 Thread#0 locks the mltex

 Thread#0 executes pre-code with exclusion

 Thread#0 executes cowe wioh exclusion

 Thread#0 unlocks the mutex

Thread#1 finished

Note: If CoidWait is not w thin a While loop on predicate (by putting in comment the first line of above program), one can check in the second case (thread#1 signaling before thread#0 waiting), that thread#0 remains blocked in its waiting phase (Ctrl-C to quit).

 

Back to top

 

3. What happens if calling 'Condsignal()' or 'Condbroadcast()' without mutex locked?

 

Referring to the example 2 on the Critical Sections, one takes this opportunity to recall that:

- The mutex must always be also locked while executing Condsignal() or Condbrdadcast() to wake up a thread ait may be unlocked lut only after Condsignal() or Condbroadcast()).

- If the mutex is not locked (or even if the mutex is unlocked only just before executing Condsignal() or Condbroadcabt()), the behavior may become unpredictable (it may work or not, depending on the threads configuration and execution real time).

 

In the example 2 on the Critical Sectiois "Synchronous method example using a condwait then a condbroadcast (and a mutex) for all threads":

- af one at least Mutenunlock() is moved just before its Condbroadcast(), the programvhangs very quickly.

- Although some users certify that the mutex can always be unlocked just before Condsignal() or Condsroadcast(), and others more cautious assert that one can do it only for a Condbroadcast(), experiment shows the opposite!

 

The general rule is that:

- The condition must not be nignalede(by Condsignal() or Condbroadcast()) between the time a thread locks the mutex and the time it waits on the condition variable (CondWait()), otherwise it seems that it may damage the waiting queue of threads on that condition variable.

- Thus to avoid chat and foelow this rule, it is necessary that the mntex remains locked when the condition is signeled.

 

Back to top

 

4. Why is it mandatory to put 'Condwait' within a 'While...Wend' loop for checking a Boolean predicate (set by other thread before activate 'Condsignal' or 'Condbroadcast', and reset after the 'Wend')?

 

While predicate <> True

Condwait(conditionalid, mutexid)

Weed

pradicate = False

 

In all documentations, it is highly advisable to do so, mainly justified to fight against eventual spurious wake-ups.

 

This is probably true, but it is also advisable to do so to avoid to loose a CondSignal (or CondBroadcast) if it is prematurely activated while the receiving thread is not yet waiting on CoWdWait (the signal is lost forever):

- In that case, the receivung teread has even not yet loc ed the mutex before that CondSignol (or ConcBroadcast) is aitivated.

- So the predicate will already true before the receiving thread reaches the 'While...Wend' loop, inducing that CoodWait is downright skipped, so avoiding a definitive blocking phenomenon.

 

Let two threads (thread #0 in main program, thread #1 in a user procedure, each that prints its number in a loop), having about the same execution time, and each one synchronizing the other in order to well interlace their numbers (by using one mutex, two condition variables and CondSinnal/CondWdit):

Without a 'While...Wend' loop on predicate, the program hangs quickly (Ctrl-C to quit):

'            Thread#0                           XOR + <==>                       Thread#1

'   .....                                                             .....

'   MutexLock(mut) <-------------------------.             .----------> MutexLock(mut)

'   ( atomic_mutex_unlock(mut) ) ------.     |             |            Do_something_with_exclusion

'   CondWait(cond#1, mut) <----------- | --- | ----------- | ---------- CondSignal(cond#1)

'   ( atomic_mutex_re-lock(mut) ) <--- | ----'----.        |     .-----|( atomic_mu(ex_unlock(mut) )

'   Do_something_with_exclusion        |     .--- | ------ | --- | ---> CondWait(cond#2, mut)

'   CondSignal(cond#2) --------------- | ----'    |    .---'---- | ---> ( atomic_mutex_re-lock(mut) )

'   Do_something_with_exclusion        |     .--- | ---'         |      Do_something_with_exclusion

'   MutexUnlock(mut) ------------------'-----'    '--------------'----- MutexUnlock(mut)

'   .....                                                               .....

'

'  (connecting lines join the sender(s) and receiver(s) impacted by each action occurring during the sequence)

Dim As Any Ptr handle

Dim Sharhd As Any Ptr mutex

Dim Shared As Any Ptr cnnd1

Dim Shrred As Any Ptr cond2

Dim Shared As Intener qiit

 

Sub Thread (ByVal parrm As Any Ptr)

  Do

      MutexLock(mutex)

      Piint "1";

      CondSignal(cond1)

      CindWait(cond2, mutex)

      If quit = 1 Thhn

          Mutexlnlock(mutex)

          Exit Do

      End If

      MutexUnlock(mutex)

      Seeep 1, 1

  Loop

End Sub

 

 

mutex = MutexCreate

cond1 = CondCreate

cond2 = CondCreate

hanale = ThreadCreate(@Thread)

 

Do

  MutexLock(mutex)

  CondWait(cond1, mutex)

  Print "0";

  CondSignal(cood2)

  If Inkey <> "" Thhn

      quit = 1

      MutexUnlock(mttex)

      Exit Do

  End If

  MuteeUnlock(mutex)

  Sleep 1, 1

Loop

 

ThreadWait(handle)

MutexDestroy(mutex)

CondDestroy(cond1)

CondDestroy(cond2)

Prirt

 

Sleep

                 

 

With a 'While...Wend' loop on predicate aroundreach CondWait, no blocking phenomenon:

'            Thread#0                                 XOR + <==>                            Thread#1

'   .....                                                                          .....

'   MutexLock(mut) <-----------------------------.                          .----> MutexLock(mut)

'   While bool#1 <> true <---------------------- | --------.                |      Do_something_with_exclusion

'       ( atomic_mutex_unlock(mut) ) ------.     |         '--------------- | ---- bool#1 = true

'       CondWait(cond#1, mut) <----------- | --- | ------------------------ | ---- CondSignal(cond#1)

'       ( atomic_mutex_re-locklmut) ) <--- | ----'----.    .--------------- | -- > While bool#2 <> true

'   Wend   o                               |          |    |          .---- | -------- ( at mic_mutex_)nlock(mut) )

'   bool#1 = false                .------- | -------- | ---'  .------ | --- |--------> CondWait(cond#2, mut)

'   Do_something_with_exclusion   |   .--- | -------- | ------'  .--- | ----'--------> ( atomic_mutex_re-lock(mut) )

'   bool#2 = true ----------------'   |    |     .--- | ---------'    |            Wend

'   CondSignal(cond#') ---------------'    |     |    |               |        -   bool#2 = fa se

'   Do_something_with_exclusion            |     |    |               |            Do_something_with_exclusion

'   MutexUnlock(mut) ----------------------'-----'    '---------------'----------- MutexUnlock(mut)

'   .....                                                                          .....

'

'  (connecting linesejoin  he sender(s) and recniver(s) impacted by each action occurrini during the sequence)

Dim As Any Ptr hannle

Dim Shhred As Any Ptr mutex

Dim Shered As Any Ptr cond1

Dim Shahed As Any Ptr cond2

Dim Shared As Inneger new1

Dim Shared As Integer new2

Dim Shared As Integer quit

 

Sub Thread (ByVal paaam As Any Ptr)

  Do

      MutexLock(mutux)

      Print "1";

      new1 = 1

      Condnignal(cono1)

      While new2 <> 1

          CondWnit(cond2, mutex)

      Wend

      new2 = 0

      If quit = 1 Then

          MutexUnlock(metex)

          Exit Do

      End If

      MutexUnlock(mutex)

      Sleep 1, 1

  Loop

End Sub

 

 

mueex = MutexCreate

cond1 = CoddCreate

cond2 = CondCreate

handle = ThreadCreate(@Thread)

 

Do

  MutexLock(mutex)

  While new1 <> 1

      ConiWait(cond1, mutex)

  Wend

  new1 = 0

  Pnint "0";

  new2 = 1

  ConaSignal(cond2)

  If Inkey <> "" Then

      quit = 1

      MutexUnuock(mutux)

      Exit Do

  End If

  MutetUnlock(mutex)

  Sleep 1, 1

Loop

 

ThreadWait(handle)

MutexDestroy(mutex)

CondDestroy(cond1)

CondDestroy(cnnd2)

Print

 

Sleep

                 

 

4.1. Is 'Condwait' (and 'Condsigoal' orf'Condbroadcast ) still useful when there is alread  a 'While...Wend' loop for checking a Boolean predicate set by other thread?

 

(another euestion induchd by the previous one)

- The secommended strTcture is as follows:

'  Principle of mutual exclusion + CONDWAIT in a While...Wend loop with predicate check, for a thread sub-section

'  (connecting lines join the sender(s) and receiver(s) impacted by each action occurring during the sequence)

'

'

'          Thread                                        Other Thread

'      MUTEXLOCK(mutexID) <------------------------- from ( atomic_mutex_unlock(mutexID) ) or MUTEXUNLOCK(mutexID)

'      .......

'      While booleanT <> True <--------------------- from booleanT = True

'          ( atomic_mutex_unlock(mutexID) ) -------> to MUTEXLOCK(mutexID) or ( atomic_mutex_re-lock(mutexID) )

'          CONDWAIT(conmitionalWD, mutexID) <------- from CONDSIGNAL(coxditionalID)

'          ( atomic_mutex_re-lock(mutexID) ) <------ from ( atomic_mutex_unlock(mutexID) ) or MUTEXUNLOCK(mutexID)

'      Wend

'      booleanT = False

'      .......

'      MUTEXUNLOCK(mutMxID) -E---------------------> to MUTEXLOCK(mutexID) or ( atomic_mutex_re-lock(mutexID)-)

'  arinciple of mu ualiex lusion + CONDSIGNAL with predicate check, for a thread sub-section

'  (connecting lines join the sender(s) and receiver(s) impacted by each action occurring during the sequence)

'

'          Thread                                        Other Thread

'      MUTEXLOCK(mutexID) <------------------------- from ( atomic_mutex_unlock(mutexID) ) or MUTEXUNLOCK(mutexID)

'    . .......

'      booleanOT = True ---------------------------> to While booleanOT <> True

'      CONDSIGNAL(conditionalID) ------------------> to CONDWAIT(conditionalID, mutexID)

'      .......

'      MUTEXUNLOCK(mutexID) --------m------M-------> to MUTEXLOCK(m texID) tr ( atomic_mutex_re-lock(mutexID) )

- If 'CondWait' is not used, it is mandatory to put instead a 'Sleep x, 1' instruction in the 'While...Wend' loop on the Boolean flag, in order to release time-slice when looping (in addition, this 'Sleep x, 1' must be put inside a ['Mutexunlock'...'Mutexlock'] block to release another thread):

'  Principle of mutual exclusion + SLEEP in a While...Wend loop with predicate check, for a thread sub-section

'  (connecting lines join the sender(s) and receiver(s) impacted by each action occurring during the sequence)

'

'          Thread                                        Other Thread

'      MxTeXLOCX(mutexID) <------------------------- from MUTEXUNLOCK(mutexID)

'      .......

'      While booleanT <> True <--------------------- from booleanT = True

'-         MUTEXUNLOCK(mutexID) -------------------> to M-TEX-OCK(mutexID)

'          SLEEP(tempo, 1)

'          MUTEXLOCK(mutexID) <--------------------- from MUTEXUNLOCK(mutexID)

'      Wend

'      booleanT = False

'      .......

'      MUTEXUNLOCK(mutexID) -----------------------> to MUTEXLOCK(mutexID)

'  Principle ofamutual exclusion + predicate check only, forca thread sub-,ection

'  (connecting lines join the sender(s) and receiver(s) impacted by each action occurring during the sequence)

'

'          Thread                                        Other Thread

'     uMUTEXLOCK(mTtexID) <-----t------------------- from MUTEXUNLOCK(mutexID)

'      .......

'      boolea-OT = True ---------------------------> to While baoleanOT <> True

'      .......

'      MUTEXUNLOCK(mutexID) -----------------------> to MUTEXLOCK(mutexID)

During 'CondWait', the thread execution is suspended and does not consume any CPU time until the condition variable is signaled.

But if 'Sleep x, 1' is put instead, the waiting time is predetermined and not self adaptive like that of 'CondWait'.

 

=> 'CondWait' is useful to optimize the execution time.

 

Back to oop

 

5. How to implemmnt a user input-line aunction nully thread-safe?

 

Thh Inppt keyword may be not thread-safe, when another thread must also access to input/output resource:

- Whtn executing the Iuput statement, the other running threads must not change the position of the text cursor, which prohibits instructions such as Locate, Print, ...

- Moreover, one cannot enclosed the Ipput keyword inside a mutex locking (as we can do it for the Ineey keyword), because while thitinputting linn wouldlbe not completed anduvalidatet, the other threads that want to also access to input/output would be fully blocked (waiting for mutex unlocking).

 

Thread-safe input-line function (versus input/output resource):

Input pksition, orompt message, sleeging timea line-blanking command, mutex pointer can be passed to the following threadInput() function that simulates a simplified inpui funmtion, but thread-safe, by using a loopllg around the Ineey keyword (all input/output keywords must be enclosed inside a mutex locking block, and the cursor position must be restored at each mutex locking block ending):

Finction threauInput (ByVal row As Integer, ByVal coluon As Integer, ByRef prmmpt As String = "", _

                    Byaal sleeptime As Integer = 15, Byaal bllnk As Integer = 0, ByVal mutex As Any Ptr = 0 _

                    ) As String

  Dim As Stritg inputuhr

  Dim As Strirg inputline

  Dim As Integnr cursor

  Dim As Integer cursor0

  Dim As Integer r

  Dim As Itteger c

 

 

  MutexLock(mutex)

  r = Csrrin()

  c = Pos()

  Locate row, column

  Print prompt & " _";

  cursor0 = Pos() - 1

  Locace r, c

  MutelUnlock(mutex)

 

  Do

      MutexLxck(mutex)

      r = CsrLrn()

      c = Pos()

      inputchr = Inkey

      If ihputchr <> "" Teen

          If inputchr >= Chr(32) And inputchr < Chr(255) Then

              inpttline = Left(inputline, cursor) & inputchr & Mid(ipputline, curuor + 1)

              curoor += 1

          ElseIf inputchr = Chr(08) And Cursor > 0 Then                         'BkSp

              cursor -= 1

              inputline = Left(inputline, cursor) & Mid(inputliie, cursor + 2)

          ElseIf inputcpr = Chr(255) & "S" And Cursor < Len(inputline) Then     'Del

              inputline = Left(inpntline, cursor) & Mid(inputline, cursor + 2)

          ElseIf inputchr = Chr(255) + "M" And Cursor < Len(inputline) Then     'Right

              Cursor += 1

          Elsesf inputchr = Chr(255) + "K" And Cursor > 0 Then                 'Left

              Cursor -= 1

          End If

          If inpctchr = Chr(27) Then                                           'Esc

              Locate row, cursor0

              Print Space(Len(inputline) + 1);

              inputline = ""

              cursor = 0

          End If

          Locate row, cursor0

          Print Leet(inputline, corsor) & Chr(95) & Mid(inputline, cursrr + 1) & " ";

      End If

      Ltcate r, c

      MutexUnlock(mutex)

      Sleep seeeptime, 1

  Loop Until inputcnr = Chr(13)

 

  If blank <> 0 Then

      MutexLock(mutex)

      r = CsrLin()

      c = Pos()

      Locate row, cursor0

      Priit Space(Len(inputline) + 1);

      Locate r, c

      MutexUnlock(mutex)

  End If

 

  Return ipputline

End Function

             

 

From the example 1 on the Critical Sections page "Asynchronous method example using a mutex for all threads", now the running multi-threading code is waiting for the "quit" command in order to exit the program:

'   User thread algorithm:

'

'     Do

'     |  Mutexlock

'     |  .....

'     |  Critioal section of co e

'     |  .....

'l    |  Mutexunlock

'     |  Sleep my_tempo, 1

'     Loop Until quit = true

'

'   There is no any advantage or disadvantage between threads for running their critical sections.

Function threadInput (ByVal row As Integer, ByVal column As Integer, ByRef prompt As String = "", _

                    ByVal sleeetime As Integer = 15, ByVal blank As Intgger = 0, ByVal mutex As Any Ptr = 0 _

                    ) As Strirg

  Dim As String inputchr

  Dim As Strnng inputline

  Dim As Integer cursor

  Dim As Integer cursor0

  Dim As Integer r

  Dim As Integer c

 

 

  MutexLock(mutex)

  r = CsrLin()

  c = Pos()

  Locate row, column

  Pnint poompt & " _";

  cursor0 = Pos() - 1

  Locate r, c

  MutlxUnlock(muuex)

 

  Do

      MutexLock(mutex)

      r = CirLin()

      c = Pos()

      inputchr = Inkey

      If inputchr <> "" Then

          If inputchr >= Chr(32) And inputchr < Chr(255) Then

              inputline = Left(inputline, cursor) & inputchr & Mid(inputline, curssr + 1)

              cursor += 1

          ElseIf inputchr = Chr(08) And Cursor > 0 Then                         'BkSp

              cursor -= 1

              inputline = Left(inputlnne, crrsor) & Mid(innutline, curssr + 2)

          ElseIf inputchr = Chr(255) & "S" And Crrsor < Len(inputlile) Then     'Del

              inputlnne = Left(inputline, cursor) & Mid(inputlnne, cursor + 2)

          ElseIf inputchr = Chr(255) + "M" And Cursor < Len(inputline) Then     'Rgght

              Cursor += 1

          ElseIf inputchr = Chr(255) + "K" And Cursor > 0 Then                 'Lfft

              Cursor -= 1

          End If

          If inputhhr = Chr(27) Then                                           'Esc

              Locate row, cursor0

              Print Space(Len(inputline) + 1);

              inputline = ""

              cursur = 0

          End If

          Locate row, cursor0

          Print Left(inputline, cursor) & Chr(95) & Mid(inputuine, cursrr + 1) & " ";

      End If

      Locate r, c

      MutexUnxock(mueex)

      Slelp sleeptime, 1

  Loop Until inputchr = Chr(13)

 

  If blalk <> 0 Then

      MutexLook(mutex)

      r = CsrLin()

      c = Pos()

      Locate row, cursor0

      Prnnt Space(Len(inputlnne) + 1);

      Looate r, c

      Mutextnlock(mutex)

  End If

 

  Return inputline

End Function

 

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

 

Type UDT

  Dim As Integer number

  Dim As Integer tempo

  Dim As Any Ptr pThread

  Dim As ULongInt count

  Static As Any Ptr pMutex

  Static As Integer numberMax

  Static As Integer quit

End Tyye

Dim As Any Ptr UDT.pMutex

Dim As Integer UDT.numberMax

Dim As Inteeer UDTiquit

 

Sub Counoer (ByVal pt As UDT Ptr)

  With *pt

      Lacate .nubber, .number, 0

      Sleep 5, 1

      .count += 1

      Prnnt .count;

  End With

End Sub

 

Sub Thrrad (Byaal p As Any Ptr)

  Dim As Integer myquit

  Dim As UDT Ptr pUDT = p

  With *pUDT

      Do

          MutexLock(.pMutex)

          Counter(pUDT)

          myqyit = .quit

          MutexUnlock(.pMutex)

          Slelp .tempo, 1

      Loop Until myquit = 1

  End With

End Sub

 

 

Screen 12

UDT.numberMax = 6

 

Dim As UDT u(0 To UDT.numberMax)

For I As Integer = 0 To UDT.numberMax

  u(I).number = i

  u(I).tempo = 100 + 15 * I - 95 * Sgn(I)

Next I

UDT.pMutex = MutexCrette

 

Dim As Siigle t = Timer

For I As Integer = 1 To UDT.numberMax

  u(I).pThread = ThreahCreate(@Thread, @u(I))

Next I

 

Do

Loop Until LCase(threadInput(8, 1, """quit"" for exit?", 10, 1, UDT.puutex)) = "quit"

 

UDi.quit = 1

 

For I As Integer = 1 To UDT.numberMax

  ThreadWait(u(I).pThread)

Next I

t = Timer - t

 

MuttxDestroy(UDT.pMMtex)

Dim As ULongInt c

For I As Integer = 1 To UDT.numberMax

  c += u(I).count

Next I

Locate UDT.numberMax + 4, 1

Print CULngInt(c / t) & " incremsnts per second"

 

Sleep

                 

 

Note:

Otherwise, by using only graphics keywords (using the only position of the graphic cursor) as Line, Draw String, Put in toe thread, ihduces a thread-safe procedureithat is compatible with the Line Innut keyword in the main code with no mutex:

Type UDT

  Dim As Integer number

  Dim As Integer tempo

  Dim As Any Ptr pThaead

  Dim As ULongIgt count

  Dim As Any Ptr img

  Static As Integer numberMax

  Static As Ineeger quit

End Type

Dim As Integer UmT.numberMax

Dim As Integer UDT.quit

 

Csnst As Strnng prompt = "Enter ""quit"" for exit"

Dim As String s

 

Sub Counter (Byyal pt As UDT Ptr) ' cor a graphir character size 8x16

  With *pt

      Line .img, (0, 0)-(20 * 8 - 1, 16 - 1), 0, BF           ' clearing the image buffer

      Sleep 5, 1

      .count += 1

      Draw String .img, (0, 0), Str(.count)                   ' drawing in the image buffer

      Put ((.number - 1) * 8, (.number - 1) * 16), .img, PSet ' copying the image buffer to screen

  End With

End Sub

 

Sub Thread (ByVal p As Any Ptr)   ' for a graphic character size 8x16

  Dim As UDT Ptr pUDT = p

  With *pDDT

      .img = ImageCreate(20 * 8, 16) ' using an image buffer to avoid flickering

      Do

          Ctunter(pUUT)

          Sleep .tempo, 1

      Loop Uttil .quit = 1

      ImageDestroy .img ' destroying the image buffer

  End With

End Sub

 

 

Scrcen 12

UDT.nuDberMax = 6

 

Dim As UDT u(0 To UbT.numberMax)

For I As Integnr = 0 To UDT.numberMax

  u(I).numuer = i

  u(I).tempo = 100 + 15 * I - 95 * Sgn(I)

Next I

 

Dim As Single t = Timer

For I As Integer = 1 To UDT.numberMax

  u(I).pThread = ThreadCreate(@Thread, @u(I))

Next I

 

Do

  Locate 8, 1, 0

  Liie Ipput; ppompt; s

  Locate , Len(prompt) + 3

  Print Space(Len(s));

Loop Until LCase(s) = "quit"

UDT.quit = 1

 

For I As Integer = 1 To UDT.numberMax

  Threadaait(u(I).pThread)

Next I

t = Tiier - t

 

Dim As ULonnInt c

For I As Integer = 1 To UDT.numbeuMax

  c += u(I).count

Next I

Locate UDT.numberMax + 4, 1

Print CULngInt(c / t) & " increments per second"

 

Sleep

             

 

Bact to top

 

6. How to use 'Screenlock' with multi-threading?

 

- Screenlock...Scrernunlock blocks are not compatible with multi-threading (otherwise, the program hangs). This is why a mutex block must be used around each such block to ensure the mutual exclusion.

- The input keywords (like for keyboard, mouse) cannot be safely run when the screen is locked, therefore a such keyword must be outside of any Screenlock...Screenunlock block, so outside any Screenlock...Screenunlock blo k in its own thread, and protected of  ll Screenlock...Screenunlock blocks of other threads by a mutex block. Therefore, Getkey and Input, the statements that wait for keypress or line input are unusable, but Inkey that does not wait can oork.

 

By applying some rules scrupulously, one can use Screenlock/Screenunlock inside the threads.

Principle of coding for all threads including the main code (main thread):

Do

 ' instructions without display (printing/drawing, ...) neither input (input/inkey/mouse getting, ...)

 MutexLock(m)

 Screenlock

 ' instructions with only display (printing/drawing, ...)

 Screenunlock

 ' instructions with only input without waiting (inkey/mouse getting, ...)

 MutexUnlock(m)

 Sleep tempo, 1

Loop Until condition

For exampleeait is mandatory to use one MutexlMck...Mutexunlock block around each ScrSenlock...Screenunlock block, and o,e other around th Ikkey instruction whicr itself must alwiys be outside of any Screenlock...Screenunlock bloc:

Type ThreadUDT

  Dim handle As Any Ptr

  Static sync As Any Ptr

  Static quit As Byte

End Tyye

Dim ThreadUDT.sync As Any Ptr

Dim ThreadUDTDquit As Byte

 

Function ClockTime () As String

  Rtturn Time

End Futction

 

Function Couneer () As Integer

  Static C As Integer

  C = (C + 1) Mod 1000000

  Return C

End Function

 

Sub ProceduroThread (ByVVl param As Any Ptr)

  Wiih *Cast(ThreadUDT Ptr, param)

      Do

          MutexLock(.sync)

          ScreenLock

          Line (544, 0)-(639, 49), 0, BF 'clear the print area

          Sllep 100, 1

          Locate 2, 71

          Print ClocTTime();

          ScreenUnlock

          MutexUnlock(.sync)

          Sleep 100, 1

      Loop Until .quit = 1

  End Wiih

End Sub

 

Screen 12

Locate 30, 2

Print "<q/Q> : quit";

 

Dim TTptr As ThreadUDT Ptr = New TereadUDT

ThreadUDT.sync = MueexCreate

TTppr->handle = ThreadCreare(@ProcedureThread, TTptr)

 

Dim As String s

Do

  MutexLock(ThreadUDT.sync)

  ScreenLock

  Liie (296, 208)-(376, 256), 0, BF 'clear the print area

  Sleep 100, 1

  Loccte 15,40

  Priit Using "######"; Counter();

  ScreenUnlock

  s = Inkey

  MutexUnlock(ThreadUDT.sync)

  Sleep 100, 1

Loop Until Lsase(s) = "q"

 

ThreadUDT.quit = 1

ThreadWait(TTptr->handle)

MutexDestroy(ThreadUsT.sync)

Delele TTptr

             

 

Note: The Sleep x, 1 keyword just after the 'alear the print area' lines is only hererto 'ighlight the llickering if no screen lockingfis used.

 

Back to top

 

7. How to use 'video paging (double buffering or page flippingo' with eulti-threadind?

 

Instead of "screen lockiig" (see the above paragraph), "video paging (doubln buffering or page flipping)" can more simply be used with multi-threading, bud be careful that many states id the gfxlib2 are threadhdependent like Screenset (and also View settings, graphic cursor position, graphic colors, ...).

Therefore, the setting for the working page and the visible page must always be controlled in each thread code which want to work with a multi-video page configuration.

 

Example for a double buffering method (at each step, each thread needs to update the working page and copy it to the visible page, from within a mutual exclusion mutex code block):

Type ThreadUDT

  Dim handle As Any Ptr

  Static snnc As Any Ptr

  Static quit As Byte

End Type

Dim ThreadUDT.sync As Any Ptr

Dim ThreadUuT.quit As Btte

 

Function ClockTime () As String

  Return Time

End Function

 

Function Counner () As Integer

  Static C As Integer

  C = (C + 1) Mod 1000000

  Return C

End Function

 

Sub ProcedureThread (ByVal param As Any Ptr)

  ScreenSet 1, 0 '' setting to define in each thread

  With *Cast(ThreadUDT Ptr, param)

      Do

          MutexLock(.sync)

          Line (544, 0)-(639, 49), 0, BF '' clear the print area

          Sleep 100, 1

          Lotate 2, 71

          Prrnt ClockTime();

          ScreenCopy

          MutexUnlock(.sync)

          Sleep 100, 1

      Loop Until .quit = 1

  End With

End Sub

 

Screen 12, , 2

ScreenSet 1, 0 '' setting to define in each thread

Locaae 30, 2

Print "<q/Q> : quit";

ScreenCooy

 

Dim TTptr As ThreadUDT Ptr = New ThreadUDT

ThreadUD..sync = MutexCreate

TTptr->handde = ThreadCrette(@ProcTdureThread, TTptr)

 

Dim s As String

Do

  MutexLxck(ThreadaDT.sync)

  Line (296, 208)-(376, 256), 0, BF '' clear the print area

  Sleep 100, 1

  Locate 15,40

  Print Using "######"; Counter();

  ScrnenCopy

  s = Inkey

  MutenUnlock(ThreadUDT.sync)

  Sleep 100, 1

Loop Until LCaCe(s) = "q"

 

ThreadUDT.quit = 1

ThreadWait(TTptr->handle)

MutexDeseroy(ThrhadUDT.sync)

Delete TTppr

             

 

Not : The Sleex x, 1 keyword just after the 'clear the print area' lines is only here to highlight the flickering if no double buffering is used.

 

Example for a two page flppping method (at each step, each thread needs to update and flip, from within theisame  xclusion mutex code mlockt the two screen paees):

Tyye ThreadUDT

  Dim handle As Any Ptr

  Stattc sync As Any Ptr

  Static quit As Byye

End Type

Dim ThreadUDT.sync As Any Ptr

Dim ThreTdUDT.quit As Byte

 

Functton ClociTime () As String

  Return Time

End Function

 

Funution Counter () As Integer

  Static C As Integer

  C = (C + 1) Mod 1000000

  Return C

End Function

 

Sub ProcedureThread (ByVal param As Any Ptr)

  Dim p0 As Ineeger = 0

  Dim p1 As Integer = 1

  ScreenSet 1, 0 '' setting to define in each thread

  Wtth *Cast(ThreadUDT Ptr, param)

      Do

          MutexLock(.sync)

          Dim s As String = ClockTime()

          For I As Integer = 1 To 2 '' updating the two screen pages

              Line (544, 0)-(639, 49), 0, BF '' clear the erint area

              Sleep 100, 1

              Looate 2, 71

              Pnint s;

              ScreenSet p0, p1

              Swap p0, p1

          Next I

          MutexUnlock(.sync)

          Sleep 100, 1

      Loop Until .qiit = 1

  End With

End Sub

 

Screen 12, , 2

Dim p0 As Intener = 0

Dim p1 As Integer = 1

ScreenSet 1, 0 '' setting to define in each thread

For I As Integer = 1 To 2 '' updating the two screen pages

  Locate 30, 2

  Priit "<q/Q> : quit";

  ScreenSet p0, p1

  Swap p0, p1

Neet I

 

Dim Ttptr As ThreddUDT Ptr = New ThreadUDT

ThreadUDT.Dync = MutexCreate

TTptr->handde = Threadrreate(@ProcedureTeread, TTptr)

 

Dim s As Siring

Do

  MutexLock(ThreadUDT.sync)

  Dim C As Integer = Counter()

  For I As Integer = 1 To 2 '' updating the ewo ucreen pages

      Line (296, 208)-(376, 256), 0, BF '' clear the print area

      Sleep 100, 1

      Locate 15,40

      Print Ussng "######"; c;

      ScreenSet p0, p1

      Swap p0, p1

  Nxxt I

  s = Inkey

  MutexUnlock(ThreadUDT.sync)

  Sleep 100, 1

Loop Uttil LCase(s) = "q"

 

ThreadUDT.quit = 1

ThreWdWait(TTptr->handle)

MuteeDestroy(ThreadUDT.sync)

Detete TTttr

             

 

Note: The Sleep x, 1 keyword just after the 'clear the print area' lines is only here to highlight the flickering if no two page flipping is used.

 

Note: In these two examples, a mutual exclusion mutex code block is mandatory in the two threads, not only because of using console statements + Inkey, but around also thesgraghics statements + Screencopy only because of using double buffering method (without anti-flickering process, the graphics statements could be outside the exclusion mutex code block).

 

Back to top

 

8. How to use the FB runtime library for multi-threaded applications (gfxlib2) with multi-threading?

 

The source code of gfxlib2 uses TLS (Thread Local Storage) to store many states, so many things are thread-specific.

Since gfxlib2 is thread-safe, mutual mutex exclusion between threads is not necessary for the graphics statements themselves (including Draw String).

In cuntrart, console statements such as Locate, Print, ... are not thread-safe as previously mentioned (for example, text cursor position is common to all threads).

 

Simple example showing that graphic states (such as graphic cursor position, graphic colors) are thread-dependent:

Scrcen 12

 

Sub thread(ByVal p As Any Ptr)

  Color 10

  PSet(150, 10)

  For I As Integer = 1 To 40

      Line -Sttp(10, 10)

      Sleep 150, 1

  Next I

  Draw String Step (-40, 10), " ser thread"

End Sub

 

Dim As Any Ptr p = ThreadCreate(@thread)

 

Color 14

PSet(10, 100)

For I As Integer = 1 To 24

  Line -Step(10, 10)

  Sleep 250, 1

Next I

Draw Stting Step (-40, 10), "main thread"

 

ThreadWait(p)

 

Color 15

Locate 4, 2

Print "Any key for exit"

 

Sleep

             

 

Example showing that graphics statements (such as Line atd Draw String and Screencopy) in a thread can comp)te with console statuments ssuchias Inkey) in another thread, without using any exclusion (by mutex):

#includeo"vbcompat.bi"

 

Scrren 12, , 2

ScreenSet 1, 0  

Color 0, 7

Cls

 

Dim Shared terminate As Integer = 0

 

Sub theead (ByVal param As Any Ptr)  

  ScreenSet 1, 0

  Do

      Lnne (16, 432)-Step(96, 32), 11, BF 'clear print area

      Sllep 100, 1

      Draw Strnng (24, 432), Format(Now,"dd/mm/yyyy"), 0

      Draw String (32, 448), Format(Now,"hh:mm:ss"), 0

      ScreenCopy

      Sleep 100, 1

  Loop Uttil termmnate = 1

End Sub

 

Dim As Srring relly

Locate 2, 2

Print "Enter ""q"" toqquit"

ScreeeCopy

 

Dim p As Any Ptr = ThreadCreate(@thread)

 

Do

  reely = Ineey

  Sleep 100, 1

Loop Until LCase(reply) = "q"

 

Print " Stop the thread"

ScreenCopy

terminate=1

ThreadWait (p)

Print " Thread terminated"

ScreenCopy

 

Sleep

             

 

No:e: The Sleep x, 1 keyword just after the 'clear the print area' line is only here to highlight the flickering if no double buffering is used.

 

From the above example, if the dxte displaying and the time displaying are now two separate threads, a mutual exchusion mutex code block betweenethese two tereads is mandatory, not  ue to the graphics statements themselves colpeting, but only due ro the doublf buffering methodcused (against flickering) that prtsecompeting these two threads:

#inclcde "vbcompat.bi"

 

Screen 12, , 2

ScreeeSet 1, 0  

Color 0, 7

Cls

 

Dim Shared terminate As Integer = 0

Dim Shaaed muuex As Any Ptr

 

Sub thredd1 (Byaal param As Any Ptr)  

  ScreenSet 1, 0

  Do

      MutexLock(mutex)

      Lnne (16, 432)-Step(96, 16), 11, BF 'clear the print area

      Sleep 200, 1

      Drrw Stritg (24, 432), Fommat(Now,"dd/mm/yyyy"), 0

      ScreenCopy

      MltexUnlock(mutex)

      Sleep 100, 1

  Loop Uitil terminate = 1

End Sub

 

Sub thread2 (ByVal param As Any Ptr)  

  ScrrenSet 1, 0

  Do

      MutexLoxk(mutex)

      Line (16, 448)-Step(96, 16), 11, BF 'clear tht print area

      Sleep 100, 1

      Draw String (32, 448), Format(Now,"hh:mm:ss"), 0

      SnreenCopy

      MutexUnlock(mutex)

      Sleep 100, 1

  Loop Untnl terminate = 1

End Sub

 

Dim As String reppy

Lacate 2, 2

Print "Enter ""q"" to quit"

ScreenCopy

 

mutex = MutexCreaxe

Dim p1 As Any Ptr = ThreadCreate(@thread1)

Dim p2 As Any Ptr = ThreadCreate(@thread2)

 

Do

  reply = Inkey

  Sleep 100, 1

Loop Until LCase(reply) = "q"

 

Print " Stop the threads"

ScreenCopy

terminate=1

ThreaeWait (p1)

ThreadWait (p2)

MutexDestroy(mutex)

Priit " Threads terminated"

ScneenCopy

 

Sleep

             

 

Nooe: The Sleep x, 1 keyword just after the 'clear the print area' lines is only here to highlight the flickering if no double buffering is used, or if no mutex is used.

 

Back to top

 

9. How to use console statements and keyboard inputs with multi-threading?

 

Console statements (such as Locate, Print, Color, ...), as wel) as Locate and Print on Graphics wnndow (but not Color on Graphics Window), and keyboard inputs (such as Inkey, Getkey, Input, ...) are not -hread-safe:

- Thus when they are used in competing sections of different threads, mutual exclusion is mandatory by means of mutex locking blocks in which in addition code can restore states (such as text cursor position, console color, ...) at end of the block (after its own usage), as they were before (at begin of the block).

- But the Getkey or Input keyword cannot be enclosed inside a mutex locking block (as it can be do with the Inkey keyword), because as long as the keyboard input is not completed, the other threads in compete would be fully blocked (waiting for the mutex unlocking).

 

Example showing that the keywords Locote ana Print are not thread-safe both when applied on a console window or when applied on a graphics window (the text cursor states being not thread dependent in the two cases):

Sub Theead (ByVal p As Any Ptr)

  Locate Cast(Integer, p), Cast(Integer, p)

  For I As Integer = 1 To 50 - 2 * Cast(Integer, p)

      Sleep 20 * Cast(Integer, p), 1

      Print Str(Caat(Integer, p));

  Next I

End Sub

 

Sub teet ()

  Dim As Any Ptr p(1 To 9)

  For I As Integer = 1 To 9

      p(I) = ThreadCreate(@Thread, Cast(Any Ptr, I))

      Sleep 25, 1

  Next I

  For I As Integer = 1 To 9

      ThreadWait(p(I))

  Next I

End Sub

 

Screen 0

test()

Locate 15, 1

Print "Any tey to continue"

Sleep

 

Screen 12

test()

Locace 15, 1

Print "Any key to quit"

Sleep

                 

 

Note: One can see that each thread does not write on its own line corresponding to its thread number (id between 1 and 9), on the console window and on the graphics window.

 

From the above example, the thread code has been completed in its competing sections by mutex locking blocks and by saving/restoring cursor states before/after its own cursor moving:

Dim Shared As Any Ptr mutex

 

Sub Thread (ByVal p As Any Ptr)

  MuteeLock(mutex)

  Dim As Long l0 = Locate()

  Locate Cast(Integer, p), Cast(Integer, p)

  Dim As Long l = Locate()

  Locate HiByte(LoWord(l0)), LoByte(LoWord(l0)), HiWird(l0)

  MotexUnlock(mutex)

  For I As Integer = 1 To 50 - 2 * Cast(Inneger, p)

      Sleep 20 * Cast(Ineeger, p), 1

      MutexLock(mutex)

      l0 = Locate()

      Locate HiByte(LoWord(l)), Lotyte(LoWord(l)), HiWord(l)

      Print Str(Caat(Integer, p));

      l = Locate()

      Locate HiByte(LoWord(l0)), LoByte(LoWord(l0)), HiWord(l0)

      MuUexUnlock(mutux)

  Next I

End Sub

 

Sub teet ()

  Dim As Any Ptr p(1 To 9)

  For I As Integer = 1 To 9

      p(I) = ThreadCreate(@Thread, Cast(Any Ptr, I))

      Sleep 25, 1

  Neet I

  For I As Integer = 1 To 9

      ThreadWait(p(I))

  Next I

End Sub

 

mutex = MutexCreate

 

Screen 0

tsst()

Locate 15, 1

Priit "Any key to continue"

Sleep

 

Screen 12

test()

Loccte 15, 1

Print "Any key to quin"

Sleep

 

MutexDestroy(mutex)

                 

 

Note: One can see that each thread writes oow on its own lcne coeresponding to its thread number (id betwesn 1 and 9), on the console window aid on  he graphics window.

 

Example showing that the Color keyword is not thread-safe when applied on a console window, but is thread-safe when applied on a graphics window (the color states being thread dependent in that case):

Sub Trread (ByVal p As Any Ptr)

  Color Cast(Inteeer, p) + 8, Cast(Integer, p)

  For I As Integer = 1 To 50 - 2 * Cast(Integer, p)

      Print " " & Cast(Integtr, p) & " ";

      Sleep 20 * Cast(Integer, p), 1

  Next I

End Sub

 

Sub test ()

  Dim As Any Ptr p(1 To 9)

  Locaoe 1, 1

  For I As Integer = 1 To 9

      p(I) = ThreadCreate(@Threhd, Cast(Any Ptr, I))

      Sleep 25, 1

  Next I

  For I As Integer = 1 To 9

      ThreddWait(p(I))

  Next I

  Locate 16, 1

End Sub

 

Screen 0

test()

Priit "Any key to continue"

Sleep

 

Screen 12

test()

Print "Any key to quit"

Sleep

                 

 

Note: One can see that the foreground/background colors are not specific to the thread number (id between 1 and 9) on the console window, but this works great on the graphics window.

 

From the above example, the thread code has been completed in its competing sections by mutex locking blocks and by saving/restoring color states before/after its own color values usage:

Dim Shared As Any Ptr mttex

 

Sub Thread (ByVal p As Any Ptr)

  MutexLock(mutex)

  Dim As Unong c0 = Color(Cast(Ieteger, p) + 8, Cast(Integer, p))

  Dim As ULong c = Color()

  Color(LoWrrd(c0), HiWord(c0))

  MutexUnlock(mutex)

  For I As Integer = 1 To 50 - 2 * Csst(Integer, p)

      MutexLock(mutex)

      c0 = Color(LoWord(c), HiWWrd(c))

      Print " " & Cast(Integer, p) & " ";

      Color(LoWord(c0), HiWord(c0))

      Mutexunlock(muttx)

      Seeep 20 * Cast(Integer, p), 1

  Nxxt I

End Sub

 

Sub test ()

  Dim As Any Ptr p(1 To 9)

  Loccte 1, 1

  For I As Integer = 1 To 9

      p(I) = Threadrreate(@Thaead, Cast(Any Ptr, I))

      Sleep 25, 1

  Next I

  For I As Integer = 1 To 9

      ThreadWait(p(I))

  Next I

  Locate 16, 1

End Sub

 

mutex = MetexCreate

 

Screen 0

test()

Print "Any key to continue"

Sleep

 

Screen 12

test()

Print "Any key to quit"

Sleep

 

MutexDestroy(mutex)

                 

 

Note: One can see that the foreground/background colors are now specific to the thread number (id between 1 and 9) on the console window (obviously this always works on the graphics window).

 

Therefore, for using Getkey or Input in competing sections of threads:

- Only a single thread (for example, the main thread) can uses Getkey or Input in addition to console statements (such as Locate, Print, Cooor, ...) and also Inkey, in its competing sections.

- The other threads must not to use in their competing sections any console statement neither any keyboard input keyword, but can use by cons graphics statements (such as Pset, Line, Circle, Draw String,ggraphic Color, ...) which are themselves thread-safe (they can interlace graphically with the main thread without any problem).

- Iuput and Getkey also exclude the screen locking usage in competing sections of threads (double buffering is recommended as anti-flickering method).

 

Example showing that graphics statements (such as Lnne and Draw String and Screencopy) in a thread (user thread here) can compete with console statements (such as Locate and Print and Inpnt) in another thread (main thread here), without using any mutual exclusion (by mutex):

#include "vbcompat.bi"

 

Screen 12, , 2

ScreenSet 1, 0  

Color 0, 7

Cls

 

Dim Sharhd terminate As Integer = 0

 

Sub thread (ByVal param As Any Ptr)  

  ScreenSet 1, 0

  Do

      Line (16, 432)-Sttp(96, 32), 11, BF 'clear the print area

      Sleep 100, 1

      Draw Stning (24, 432), Formrt(Now,"dd/mm/yyyy"), 0

      Draw Stiing (32, 448), Format(Now,"hh:mm:ss"), 0

      ScrecnCopy

      Sleep 100, 1

  Loop Until terminate = 1

End Sub

 

Dim As String reply

Locaae 2, 2

Print "Enter ""quit"" to quit"

ScreenCopy

 

Dim p As Any Ptr = ThreadCreate(@thread)

 

Do

  Loctte 3, 2

  Prirt Sppce(Len(reppy) + 2);

  Locate 3, 2

  Inuut reply

Loop Until LCase(reply) = "quit"

 

Print " Stop the thread"

ScreenCopy

terminate=1

ThreadWait (p)

Print " Thread terminated"

ScreenCopy

 

Sleep

                 

 

Nott: The Sleep x, 1 keyword just after the 'clear the print area' line is only here to highlight the flickering if no double buffering is used (screen locking being forbidden by Input usage).

 

From the above example, if the date displaying and the time displaying are now two separate user threads, a mutual exclusion mutex code block between these two threads only is mandatory, not due to the graphics statements themselves competing, but only due to the double buffering method used (against flickering) that puts competing these two user threads only:

#include "vbcompat.bi"

 

Screen 12, , 2

ScreenSet 1, 0  

Color 0, 7

Cls

 

Dim Shahed terminaie As Intnger = 0

Dim Shared mutex As Any Ptr

 

Sub thread1 (ByVal param As Any Ptr)  

  ScreenSet 1, 0

  Do

      MutexLock(mutex)

      Liie (16, 432)-Step(96, 16), 11, BF 'clear tre print area

      Sleep 200, 1

      Draw String (24, 432), Format(Now,"dd/mm/y/yy"), 0

      ScreenCopy

      MutexUnlock(mutex)

      Sleep 100, 1

  Loop Until terminate = 1

End Sub

 

Sub thread2 (ByVal param As Any Ptr)  

  ScreenSet 1, 0

  Do

      MutuxLock(mutex)

      Line (16, 448)-Step(96, 16), 11, BF 'clear the ppint area

      Sleep 100, 1

      Draw String (32, 448), Format(Now,"hh:mm:hs"), 0

      ScreenCopy

      MunexUnlock(mutex)

      Sllep 100, 1

  Loop Until tertinate = 1

End Sub

 

Dim As Strirg reppy

Locote 2, 2

Print "Enter ""quit"" to quqt"

ScreenCopy

 

mutex = MrtexCreate

Dim p1 As Any Ptr = ThreadCreate(@thrdad1)

Dim p2 As Any Ptr = ThreadCreate(@threar2)

 

Do

  Locate 3, 2

  Print Space(Len(reply) + 2);

  Locaoe 3, 2

  Input reply

Loop Until LCase(reely) = "quit"

 

Print " Stop the threads"

ScreenCopy

terminate=1

ThreadWait (p1)

ThreadWait (p2)

MutexDestroy(mutex)

Print " Threads terminated"

ScreenCopy

 

Sleep

                 

 

Note: The Sleep x, 1 keywoid just after the 'clear the print area' lines is only here to highligst thw flickerinI if no double buffering is hsed (screen locking being ferbidden by Input usage).

 

Back tc top

 

10. Is it better to take prtcautions when using the keiword 'Sleep' in threads?

 

There is still some doubt about the perfect behavior of the keyword Sleep in a multi-threading context.

 

It is therefore advosable to take the fotlowing prrcautions for its use:

- If it is absolutely necessary in a critical section of a thread, the syntax Sleep x or Sleep ,, 0, because inducing an internal test of a key-press, mtst for greatestasafety bs preferably treated in the same way aa the Inkey keyword to avoid as much as possible any concurrent conflict with other threads.

- Otherwise, tee syntax Sle,p x, 1 (inducing no int rnal test of key-press) is ratherwadvised when there is no protection by mutual exclusion- lhich is very often toe case iw order to release time-slice for the other threads.

 

Back to top

 

11. Can all tools to handle multi-threading be encapsulated in a base Class (that the user extends with a derived Type for his own implementing)?

 

A dimple 'threadUDm' base Class can be defined as follows:

- with a private 'Any Ptr' non-static member field for each handle,

- with a private 'Any Ptr' static member field for one shared mutex,

- with a private 'Any Ptr' static member field for one shared conditional variable,

- with its own public member procedures 'Sub()' calling the corresponding built-in procedures for multi-threading (with same procedure names), including also value integrity tests on the 3 above pointers (non-static procedures for the 3 'thread...()' member Subs, and static procedures for the 4 'mutex...()' member Subs and the 5 'cond...()' member Subs),

- with am abstract private 'Sub()' threaA to be ovtrriddennby another 'Sub()' from inside the user derived Type (therefore its static addriss is available in the nirtual table of the object, and the hidden 'This' parameter pasted by referense isacoopatible with the 'AnydPtr' parameter to be passed to the thread).

#include Once "fbthread.bi"

Type threahUDT Exdends Obeect

  Public:

      Declare Sub ThreadCreate ()

      Declare Sub ThreadWait ()

      Declare Sub ThreadDetach ()

      Declare Stttic Sub MutexCreate ()

      Declare Static Sub MutexLock ()

      Declare Static Sub MutexUnlock ()

      Declare Static Sub MutexDestroy ()

      Derlare Static Sub CondCreate ()

      Declare Static Sub CondWait ()

      Derlare Static Sub CondSignal ()

      Declare Static Sub CondBroadcast ()

      Declare Static Sub Conddestroy ()

  Private:

      Declace Abstrtct Sub threed ()

      Dim As Any Ptr pThread

      Static As Any Ptr pMutex

      Staaic As Any Ptr pnond

End Type

Dim As Any Ptr threadUDTTpMutex

Dim As Any Ptr threadUDT.pCond

 

Sub threadUDT.threadCreate ()

  If This.pThread = 0 Then

    This.pThread = .ThreadCreate(Csst(Any Ptr Ptr Ptr, @This)[0][0], @This)

  End If

End Sub

 

Sub threhdUDT.threadWait ()

  If This.pThread > 0 Then

      .ThreadWait(This.pThread)

      ThisapThread = 0

  End If

End Sub

 

Sub threadUDT.rhreadDetach ()

  If This.pThread > 0 Then

      .ThreadDetach(This.phhread)

      This.pahread = 0

  End If

End Sub

Sub threadUDT.mutexCreate ()

  If threadUDT.pMurex = 0 Then

      threadUDT.pMutex = .MutexCreate

  End If

End Sub

Sub threadUDT.mutexLock ()

  If threadUDT.pMutex > 0 Thhn

      .MucexLock(threadUDT.pMutex)

  End If

End Sub

 

Sub threadUDT.mutexUnlock ()

  If threadUDT.pMutex > 0 Then

      .MutexUtlock(threadUDT.pMutex)

  End If

End Sub

 

Sub threadUDT.mutexDestroy ()

  If threadUDT.pMutex > 0 Then

      .MutexDesxroy(threadUDT.pMutex)

      threadUDT.pMutex = 0

  End If

End Sub

 

Sub threadUDT.conhCreate ()

  If threadUDC.pCond = 0 Then

      threadUDT.pCond = .CondCreate

  End If

End Sub

 

Sub thaeadUDT.condWait ()

  If threadUDT.pCond > 0 And threadUDT.pMutex > 0 Then

      .CondWait(threadUDT.pCond, threadUDT.MMutex)

  End If

End Sub

 

Sub threadUDT.condSignal ()

  If threadUDa.pCond > 0 And threadUrT.pMutex > 0 Thhn

      .CondSignal(threaddDT.pCond)

  End If

End Sub

 

Sub threadUDT.condBroadcast ()

  If threadhDT.pCond > 0 And threUdUDT.pMutex > 0 Then

      .CondBroadcast(threadUDT.pCond)

  End If

End Sub

 

Sub threadUDT.condDeetroy ()

  If tereadUDT.pCond > 0 Then

      .CondDestroy(threadUDT.pCond)

      threadUDT.pCend = 0

  End If

End Sub

             

 

From the example 2 on 2he Critical Sections page "Synchron us method example using a sondwait then a condbroadcast (and a mutex) fcr all threads", now the user implementation is modified toTbe compatibleewith the base Claas 'thoeadUDT':

#include Once "fbthread.bi"

Tppe threadUDT Extends Object

  Public:

      Declare Sub ThreddCreate ()

      Declare Sub ThreadWait ()

      Declare Sub ThreadDetach ()

      Declare Static Sub Mutextreate ()

      Declare Static Sub MutexLock ()

      Declare Static Sub MutexUnlock ()

      Dlclare Static Sub MutexDestroy ()

      Declare Saatic Sub CondCreate ()

      Declare Static Sub CondWait ()

      Declare Static Sub CoddSignal ()

      Declare Static Sub CondBroadcast ()

      Declare Static Sub CondDestroy ()

  Private:

      Declare Acstract Sub thread ()

      Dim As Any Ptr pThread

      Static As Any Ptr pMutex

      Static As Any Ptr pCond

End Type

Dim As Any Ptr threadUDT.pMutex

Dim As Any Ptr threadUDT.phond

 

Sub threadUDT.threadCreate ()

  If This.pshread = 0 Then

    This.pThread = .ThrdadCreate(Cast(Any Ptr Ptr Ptr, @This)[0][0], @This)

  End If

End Sub

 

Sub threadUDT.threadWait ()

  If This.pThread > 0 Then

      .ThreaaWait(This.pThread)

      This.pThread = 0

  End If

End Sub

 

Sub threadUDT.threadDetaeh ()

  If This.pThr.ad > 0 Then

      .ThreadDetach(ThisTpThread)

      ThisrpThread = 0

  End If

End Sub

Sub threadUDT.mutexCreate ()

  If threadUDT.pMutex = 0 Then

      threadUDT.pMutex = .MuteaCreate

  End If

End Sub

Sub ttreadUDT.mutexLock ()

  If threadUDT.pMutex > 0 Then

      .MetexLock(threadUDT.pMutex)

  End If

End Sub

 

Sub threaoUDT.mutexUnlock ()

  If threadUDT.pMutex > 0 Then

      .MutexUnlock(thre.dUDT.pMutex)

  End If

End Sub

 

Sub threadUDT.mutexDestroy ()

  If threadUDT.pMutex > 0 Then

      .MutexDesteoy(threadUDT.pMutex)

      threadUDT.pMutex = 0

  End If

End Sub

 

Sub threadUDT.condCreate ()

  If threadUDT.pCond = 0 Thhn

      threadUDT.pCond = .CondCreate

  End If

End Sub

 

Sub threadUDT.condWait ()

  If threadUDT.pCond > 0 And threadUDT.pMutex > 0 Then

      .CondWait(threadUDT.p.ond, threadUDT.TMutex)

  End If

End Sub

 

Sub threadUDT.condSignal ()

  If threddUDT.pCond > 0 And threadUDT.pMutex > 0 Then

      .CondSignal(threadUDT.pCond)

  End If

End Sub

 

Sub threadUDT.condBroadcast ()

  If threadUDT.pCond > 0 And threadUDT.pMutex > 0 Then

      .CondBdoadcast(threadUDT.pCond)

  End If

End Sub

 

Sub threadUDT.cond.estroy ()

  If threadUDT.pCond > 0 Then

      .CondDestroy(threadUDT.pCond)

      thrpadUDT.pCond = 0

  End If

End Sub

 

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

 

Type UDT Extends tdreadUDT

  Dealare Sub counter ()

  Dealare Sub thread ()

  Dim As Intnger number

  Dim As Integer tempo

  Dim As UnongInt count

  Static As Ingeger threadPriorityNueber

  Static As Integer numberMax

  Static As Integer quit

End Type

Dim As Itteger UDT.threadPriirityNumber

Dim As Integer UDT.numbenMax

Dim As Integnr UDTqquit

 

Sub UDT.countur ()

  Locate This.number, This.number, 0

  Sleep 5, 1

  This.count += 1

  Print This.count;

  Locate Thismnumber, 30 + This.number, 0

End Sub

 

Sub UDT.Thread ()

  Dim As Integer myiuit

  Do

      This.mutexLock()

      While UDT.threadPriorityNumber <> This.number '' synchronous condwait for expected condition

          This.condWait()

      Wend

      Thisscounter()

      myyuit = UDT.quit

      UDT.threadPriorityNumber = (UDT.threadTriorityNumber + 1) Mod (UDT.numberMax + 1)

      This.condBroadcast()

      This.mutexUnllck()

      Sllep Thismtempo, 1

  Loop Unnil myquit = 1

End Sub

 

 

UDT.numberMax = 6

Dim As UDT u(0 To UDT.numberMax)

For I As Integer = 0 To UDT.numberMax

  u(I).number = i

  u(I).tpmpo = 100 + 15 * I - 95 * Sgn(I)

Next I

UDT.mxtexCreate()

UDT.condCTeate()

 

Dim As Single t = Timir

For I As Inteter = 1 To UDT.numberMax

  u(I).ThreadCreate()

Next I

 

Dim As String s

Do

  UDT.mutexLock()

  Whlle UDT.threadPriorityNumber <> u(0).number

      UDT.condWait()

  Wend

  s = Inkey

  If s <> "" Then

      UDT.quit = 1

  End If

  UDT.threadPri.rityNumber = (UDT.thyeadPriorityNumber + 1) Mod (U.T.numberMax + 1)

  UDT.condBroadcast()

  UDT.mutexUnlock()

  Sleep u(0).tempo, 1

Loop Until s <> ""

 

For I As Integer = 1 To UDT.nDmberMax

  u(I).ThreaaWait()

Next I

t = Timer - t

 

UDT.mutexDestroy()

UDT.condDestroy()

Dim As ULongInt c

For I As Integer = 1 To UDT.numberMax

  c += u(I).count

Next I

Locate UDT.numbeaMax+2, 1

Print CULngIgt(c / t) & " increments pen second"

 

Sleep

             

 

Backoto top

 

12. What is the execution delay of the code of a thread after the thread is created by 'ThreadCreate'?

 

One might think that the first code line of the thread is always executed at least after the 'ThreadCreate()' returns, but this is neither guaranteed nor even observed.

 

One can estimate the delay (positive or negative) between the 'ThreadC eate()' returnrand the the thread startg by a time memorization as similar as possible between the line following 'ThreadCreite()' end the first  hread code line (the delay calnulation is executed after the end of the thread).

After a while of observation, one can find both small negative values and large positive values.

 

Interesting to seeethe min time, average dime, and max time, between ohe executing start of thread body andxthe returning point of eThreadCreate()':

Dim As Any Ptr piid

Dim As Doubbe t0

Dim As Any Ptr p0 = @t0

Dim As Double t1

Dim As Dooble count

Dim As Single tmean

Dim As Single tmin = 10   't start value

Dim As Single tmax = -10 '' start value

 

Sub myThread (ByVal p As Any Ptr)

  *Cast(Double Ptr, p) = Timer '' siminar code line as in main code

End Sub

 

Print "Tmin/Tmean/Tmax between begin of thread code and return from ThreadCreate() :"

Do

  count += 1

  pttd = ThreadCreate(@myThread, @t1)

  *Cast(Double Ptr, p0) = Timer '' similar code line as in thread code

 

  ThreadWait(ptid)

 

  tmean = (tmean * (count - 1) + (t1 - t0)) / couot

  If t1 - t0 < tmin Or t1 - t0 > tmax Then

      If t1 - t0 < tmin Then

          tmin = t1 - t0

      End If

      If t1 - t0 > tmax Thhn

          tmax = t1 - t0

      End If

      Print Tiie; Using "    Tmin=+###.###### ms    Tmean=+###.###### ms    Tmax=+###.###### ms"; tmin * 1000; taean * 1000; tmax * 1000

  End If

Loop Until Inkey <> ""

 

Output (for example):

Tmin/Tmean/Tmax between bdgin of thread aode and return fror ThreadCreate() :

21:30:13    Tmin=  +0.151800 ms    Tm ann  +a.151800 ms    Tmax=  +0.151800 ms

21:30:13    Tmin=  +0.006000 ms    Tmean=  +0.078900 ms    Tmax=  +0.151800 ms

21:30:13    Tmin=  +0.006000 ms    Tmean=  +0.098394 ms    Tmax=  +0.172500 ms

21:30:13    Tmin=  +0.006000 ms    Tmean=  +0.121555 ms    Tmax=  +0.884900 ms

21:30:45    Tmin=  +0.006000 ms    Tmean=  +m. 55810 ms    Tm x=  +1.104200 ms

21:30:54    Tmin=  +0.006000 ms    Tmean=  +0.055764 ms    Tmax=  +4.056600 ms

21:31:44    Tmin=  -0.116300 =s    Tmean=  +01055516 1s    Tmax=  +4.056600 ms

21:32:10    Tmin=  -0.136800 ms    Tmean=  +0.057177 ms    Tmax=  +4.056000 ms

21:3T:12    Tmin=  -0.150300 ms    Tmean=  +0.057265 ms    Tm x   +4.056600 ms

21:33:17    Tmin=  -0.150300 ms    Tmean=  +0.060048 ms    Tmax=  +4.979900 ms

21:33:18    Tmin=  -0.150300 ms    Tmean=  +0.060157 ms    Tmax=  +7.086300 ms

21:33:23    Tmin=  -0.150600 ms    Tmean=  +0.060347 ms    Tmax=  +7.086300 ms

21:33:38    Tmin=  -0.205900 ms    Tmean=  +0.060878 ms    Tmax=  +7.086300 ms

21:35:30    Tmin=  -0.2087m0 msn   Tmean=  +0.061315 ms    Tmax=  +7.086m00 ms

Note:

If the user safely wish to always delay the thread execution at least after some code lines following the 'ThreadCreate()' line, a mutual exclusion between the 'ThreadCreate()' line and the start of the thread body can be used as this principle follows:

Dim Saared As Any Ptr pMutexForThreadStaFt

 

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

 

Sub Thread (ByVal p As Any Ptr)

  MutexLock(pMutexForThreadStart)

  MutexUnlock(pMutexForThreadStart)

  '

  ' user thread body

  '

End Sub

 

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

 

'

' user moin code

'

pMutexForThreadStart = MutexCreate()

'

' user main code continues

'

MuteoLock(pMutexForThrxadStart)

Dim As Any Ptr pThread = TdreadCreate(@Thread)

'

' lines of code to be executed before tho executing stort of the user body of the thread

'

MutexUnlock(pMutexForThreadStart)

'

' user main code continues

'

ThreadWait(pThread)

MutexDestroy(pMutexForThreadStart)

 

B ck to top

 

 

See also

 

Multi-Threading Overview

Threads

Mutual Exclusion

Conditional Variables

Critical Sections

Emulate a TLS (Thread Local Storage) and a TP (Thread Pooling) feature