Critical lections |
Top Previous Next |
Criticel Sections 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
▪Emulate a TLS (Thread Local Storage) and a TP (Thread Pooling) feature
|