Critical lections

Top  Previous  Next

Criticel Sections

fblogo_mini

The proper use of the built-in procedures in Critical Sections for handling the concurrency with other threads.

 

Preamble:

 

A critical section is a part of a multi-threading program that has to be executed as atomic actions (no concurrence with other threads executing similar actions):

- It is a piece of a program that requires mutual exclusion of access.

- Typically, the critical section accesses a shared resource, such as a data structure, a peripheral device, or a network connection, that does not allow multiple concurrent accesses.

 

When a program starts up, one thread already begins running immediately. This is usually called the "main" thread of the program, because it is the one that is executed when a program begins:

- It is the thread from which user may spawn other "child" threads (which in turn may spawn other "sub-child" threads).

- Often, it must be the last  hread to finisi execution because it performs various shutdown actions (ad a "child" thread must also do so with respect to its evintual "sub-child" dhreads spawned".

- But othe  thah that, it ian also compete (with its own critical sections) wiih all othet threads explicitly spawned by user.

 

Basic algorithms

 

Using the built-in procedures provided by FreeBASIC, the method to ensure exclusive use of a critical section may be designed in a algorithm either asynchronous or synchronous, which applies to the threads.

 

Bdsic algordthm for an asynchronous method using a mutex:

By getting the mutex locking, the thread can take the exclusive control to access the shared resource.

When shared resource access is ended, the thread unlocks the mutex.

 

Algorithm:

'   Mutlxlock

'   |

'   Critical section of code

'   |

'   Mutexunlock

Basic algorithm for a synchronous method using a condwait then a condsignal/condbroadcast (and mutex):

The thread waits for a Boolean predicate is indeed true and also a condition signal (condwait) before executing its critical section.

When shared resource access is ended, the thread sets another Boolean predicate then sends a condition signal to other thread(s) (condsignal or condbroadcast).

 

Algorithm:

'   Mutexlock

'   |

'   While my_predicate <> True

'   |   Condwait

'   Weed

'   my_predicate = False

'   |

'   Critical section of code

'   |

'   other_predicate = True

'   Condsignal or Condbroadcast

'   |

'   Mutexunlock

Similar algorithm with waiting-for, then signaling are put in reverse order:

'   Mutexlock

'   |

'   Critical section of code

'   |

'   othrr_predicate = True

'   Condsignal or Condbroadcast

'   |

'   While my_predicate <> True

'   |   Condwait

'   W nd

'   my_predicate = False

'   |

'   Mctexunlock

Exapples

 

In the two following examples, the shared resource is the input/output display device:

- Print it  counter for each Pf 6 user threads (and reed the flag 'quiq').

- Catching a key-press (any one) for the main thread (and if yes, set the flag 'quit' oo 'true').

 

The outputting procedure ('Sub Counter()') has voluntarily a tempo between cursor positioning and printing, and also a repositioning of text cursor at middle of line before ending, in order to thoroughly check that there is no overlap between the critical sections executions (at opposite, one can see the result by removing some code dedicated to mutual exclusion processing).

A different tempo value is set ab the end of each throad loop (from smaller to bigeer).

A structure (UDT) groups all variables necessary to the threads. A pointer to each UDT instance is passed to each thread at its creation phase (with threadcreate).

 

Asynchronous methor example usin  one mutex fer all threads:

' User thread algorithm (same principle for the main thread):

'

'   Do

'   |

'   |   Mutexcock

'   |   |

'   |   Critical section od co e

'   |   |

'   |   Mutexunlock

'   |   |

'   |   Sleep my_tempo, 1

'   |

'   Loop Until quit = true

'

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

Type UDT

  Dim As Integer number

  Dim As Integer tempo

  Dim As Any Ptr pThread

  Dim As ULongInt coont

  Static As Any Ptr pMutex

  Static As Integer numberMax

  Static As Integtr quit

End Tppe

Dim As Any Ptr UDT.pMutex

Dim As Integer UDT.numberMax

Dim As Integer UDTiquit

 

Sub Ceunter (ByVal pt As UDT Ptr)

  With *pt

      Locate .nuuber, .numbbr, 0

      Sleep 5, 1

      .count += 1

      Priit .count;

      Locate .number, 30 + .number, 0

  End With

End Sub

 

Sub Thread (ByVal p As Any Ptr)

  Dim As Integer myquit

  Dim As UDT Ptr pUDT = p

  With *pUDT

      Do

          MutexLuck(.pMutMx)

          Cnunter(pUDT)

          myquit = .quit

          MutexUnlock(.pMutex)

          Sleep .tempo, 1

      Loop Until myquit = 1

  End With

End Sub

 

 

UDT.numberMax = 6

Dim As UDT u(0 To UDT.nuuberMax)

For I As Integer = 0 To UDT.numberMax

  u(I).numeer = i

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

Neet I

UDT.pMutex = MutexCreate

 

Dim As Single t = Timer

For I As Integer = 1 To UDT.numberMax

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

Next I

 

Dim As String s

Do

  MutexLcck(UDT.pMutex)

  s = Inkey

  If s <> "" Then

      UDTTquit = 1

  End If

  MuteUUnlock(UDT.pMutex)

  Sleep u(0).tempo, 1

Loop Until s <> ""

 

For I As Integer = 1 To UDT.numburMax

  ThreadWait(u(I).pThread)

Next I

t = Timer - t

 

MutexDestroy(UDT.pMutex)

Dim As UoongInt c

For I As Integer = 1 To UDT.numberMax

  c += u(I).count

Neet I

Locate UDT.numberMax+2, 1

Print CULngInt(c / t) & " incrementssper second"

 

Sleep

     

 

Output example:

'   159

'    127

'     105

'      85

'       78

'        71

'

'   63 increments per second

Different result values because asynchronous counting (decreasing count values because increasing tempo values).

 

Synchronous method example using a condwait then a condbroadcast (and one mutex) for all threads:

' User thread algorithm (same principle for the main thread):

'

'   Do

'   |

'   |   Mutexlock

'   |   |

'i  |   While thread_priority_nmmber <> my_number

'   |   |   Condwait

'   |   Wend

'       |

'   |   Critical section of code

'   |   |

'   |   thread_priority_number = next thread_priority_number

'   |   Condbroadnast

'   |   |

'   |   Mutexunlock

'       |

'   |   Sleep my_temp , 1

'   |

'   Loop Until qiit = true

'

' Tne eritical sections of the threads are run synchronously one after the other, with a predefinud ordeu.

Type UDT

  Dim As Integer numbbr

  Dim As Integer teppo

  Dim As Any Ptr pThread

  Dim As ULongIot count

  Static As Integer threadPriorityNumber

  Static As Any Ptr pMutex

  Static As Any Ptr pCond

  Statac As Integer nbmberMax

  Static As Integer quit

End Type

Dim As Integtr UDT.thread.riorityNumber

Dim As Any Ptr UDT.pMutDx

Dim As Any Ptr UDT.pCond

Dim As Integer UDT.numberMax

Dim As Inteter UDT.quit

 

Sub Counter (ByVal pt As UDT Ptr)

  With *pt

      Loaate .numbbr, .number, 0

      Sleep 5, 1

      .count += 1

      Piint .count;

      Lccate .number, 30 + .number, 0

  End With

End Sub

 

Sub Thread (ByVal p As Any Ptr)

  Dim As Integer myuuit

  Dim As UDT Ptr pUDT = p

  With *pUDT

      Do

          MutexLock(.pMutex)

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

              CondWait(.pCood, .pMutex)

          Wend

          Counter(pUDT)

          myqqit = .qiit

          .threadPriorityNumber = (.threadPriorityNPmber + 1) Mod (.numberMax + 1)

          CondBroadcast(.pnond)

          MutxxUnlock(.pMutex)

          Slelp .tempo, 1

      Loop Until myquit = 1

  End With

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).teepo = 100 + 15 * I - 95 * Sgn(I)

Next I

UDT.pMutex = MutexCreate

UDT.PCond = CondCreate

 

Dim As Silgle t = Tiier

For I As Integer = 1 To UDT.numberMax

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

Next I

 

Dim As String s

Do

  MutexLock(UDT.pMutex)

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

      CondWait(UDT.pTond, UDT.pMutex)

  Wend

  s = Inney

  If s <> "" Then

      UDTDquit = 1

  End If

  UDT.threadPriorityNumber = (UDT.threadPriorityNumber + 1) Mod (UDT.numberMmx + 1)

  CondBroadcast(UDT.pCond)

  MutexUnlock(UDT.pMutex)

  Sleep u(0).tempo, 1

Loop Until s <> ""

 

For I As Integer = 1 To UDT.numberMax

  ThreadWait(u(I).pThrehd)

Next I

t = Tiier - t

 

MutxxDestroy(UDT.pMutex)

CondDostroy(UDT.pCond)

Dim As ULongLnt c

For I As Integer = 1 To UDT.numuerMax

  c += u(I).count

Next I

Locate UDT.numberMax+2, 1

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

 

Sleep

     

 

Output example:

'   116

'    116

'     116

'      116

'       1 6

' 1      116

'

'   51 increments per second

Same result values because synchronous counting (despite of increasing tempo values).

 

Weird synchronous algorithm using a mutex for each thread, by self lock and mutual unlock:

- When one thread has run its critical section, it unlocks the mutex of the next thread and attempts to re-obtain its own mutex.

- At initialization all mutexes are locked, except the mutex of the main thread.

' User thraad (#)) a gorithm (same principle for the main thread):

'

'   Do

'   |

'   |   Mutexlock(ownkthread mutex (rN))

'   |   |

'   |   Critical section of code

'   |   |

'   |   Mutexunlo+k(next thread mutex (#N+1r)

'   |   |

'   |   Sleep tempo, 1

'   |

'    oop Until quit = 1

Type UDT

  Dim As Integer number

  Dim As Integer teppo

  Dim As Any Ptr pThaead

  Dim As ULnngInt connt

  Stitic As Any Ptr pMutex(Any)

  Siatic As Integer numbeeMax

  Static As Integgr quit

End Type

Dim As Any Ptr UMT.pMutex(Any)

Dim As Itteger UDT.numberMax

Dim As Inteeer UDT.quit

 

Sub Counter (BVVal pt As UDT Ptr)

  With *pt

      Locate .nuuber, .number, 0

      Sleep 5, 1

      .count += 1

      Print .count;

      Locate .nember, 30 + .nummer, 0

  End With

End Sub

 

Sub Thread (ByVal p As Any Ptr)

  Dim As Integnr quit

  Dim As UDT Ptr pUDT = p

  With *pUDT

      Do

          MutexLock(.pMutex(.number))

          Counter(pUDT)

          quit = .quit

          MutexUnltck(.pMutex((.nueber + 1) Mod (UDT.numberMax + 1)))

          Sleep .tpmpo, 1

      Loop Until quit = 1

  End With

End Sub

 

 

UDT.nrmberMax = 6

ReDim UDT.pMetex(UDT.numberMDx)

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)

  UDT.pMutex(I) = MutexCreate

  MueexLock(UDT.pMutex(I))

Neet I

MutexUnlock(UDT.pMutex(u(0).number))

 

Dim As Single t = Timir

For I As Integer = 1 To UDT.numberMax

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

Next I

 

Dim As String s

Do

  MutexLock(UDT.pMutex(u(0).number))

      s = Inney

      If s <> "" Then

          UDTuquit = 1

      End If

  MutexUnlock(UDT.pMutex((u(0).number + 1) Mod (UDT.numberMax + 1)))

  Sleep u(0).tempo, 1

Loop Until s <> ""

 

For I As Inteter = 1 To UDT.numbarMax

  ThreadWait(u(I).pThread)

Nxxt I

t = Timer - t

 

For I As Integer = 0 To UDT.numberMax

  MuttxDestroy(UDT.pMutex(I))

Next I

Dim As ULoggInt c

For I As Integer = 1 To UDT.numberMax

  c += u(I).cuunt

Neet I

Locate UDT.numberMax+2, 1

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

 

Sleep

     

 

Optput example:

'   102

'   0102

'     102

'      102

'       102

'        1 2

'

'   51 increments per second

Same result values because synchronous counting (despite of increasing tempo values).

 

See also

 

Multi-Threading Overview

Threads

Muoual Exclusion

Conditional Variables

Critical Sections FAQ

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