How to Manage a Critical Section of the code of a Thread in FB
Re: How to Manage a Critical Section of the code of a Thread in FB
I see it mostly as a warning: freebasic's variable length strings and multithreading don't work well together.
An example of a generated crossword for which I am now using multithreading as I ran out out ideas to speed up the code otherwise: https://github.com/verybadidea/crosswor ... -02-13.png
An example of a generated crossword for which I am now using multithreading as I ran out out ideas to speed up the code otherwise: https://github.com/verybadidea/crosswor ... -02-13.png
-
- Posts: 4699
- Joined: Jan 02, 2017 0:34
- Location: UK
- Contact:
Re: How to Manage a Critical Section of the code of a Thread in FB
A comment worth remembering.badidea wrote:freebasic's variable length strings and multithreading don't work well together.

Re: How to Manage a Critical Section of the code of a Thread in FB
Maybe to add: If speed gain is your goal.
Re: How to Manage a Critical Section of the code of a Thread in FB
and if at least two threads (including the main thread) use var-len strings.
-
- Posts: 4699
- Joined: Jan 02, 2017 0:34
- Location: UK
- Contact:
Re: How to Manage a Critical Section of the code of a Thread in FB
That has set 'the cat amongst the pigeons' rendering 'fix-len strings' to never get a look in.fxm wrote:and if at least two threads (including the main thread) use var-len strings.
-
- Posts: 4699
- Joined: Jan 02, 2017 0:34
- Location: UK
- Contact:
Re: How to Manage a Critical Section of the code of a Thread in FB
@badidea
I had a look at your github link.
If you have time, try the following.
Download CryptoRndIISource.zip and unzip to get the bas file.
Now try the following.
--------------------
Declare Function settimer Lib "winmm" Alias "timeBeginPeriod"(As Ulong=1) As Long
settimer
#include "CryptoRndIISource.bas" ' Using your path
#undef Rnd
#define Rnd cryptos
<Your generated crossword source>
--------------------
In 32-bit mode your generated crossword binary will increase by 19KiB.
In theory, generated crossword should get a noticeable performance boost. Don't worry if it does not - without analysing your code, I cannot tell whether it will be noticeable or not.
I had a look at your github link.
If you have time, try the following.
Download CryptoRndIISource.zip and unzip to get the bas file.
Now try the following.
--------------------
Declare Function settimer Lib "winmm" Alias "timeBeginPeriod"(As Ulong=1) As Long
settimer
#include "CryptoRndIISource.bas" ' Using your path
#undef Rnd
#define Rnd cryptos
<Your generated crossword source>
--------------------
In 32-bit mode your generated crossword binary will increase by 19KiB.
In theory, generated crossword should get a noticeable performance boost. Don't worry if it does not - without analysing your code, I cannot tell whether it will be noticeable or not.
-
- Posts: 4699
- Joined: Jan 02, 2017 0:34
- Location: UK
- Contact:
Re: How to Manage a Critical Section of the code of a Thread in FB
Apologies - there was a typo in the link. 

Re: How to Manage a Critical Section of the code of a Thread in FB
The performance of Rnd is irrelevant for my application. It would save microseconds on a minutes (of even hours) timescale.
@moderator: This seems to go off-topic.
@moderator: This seems to go off-topic.
Last edited by badidea on Feb 16, 2025 10:19, edited 1 time in total.
-
- Posts: 4699
- Joined: Jan 02, 2017 0:34
- Location: UK
- Contact:
Re: How to Manage a Critical Section of the code of a Thread in FB
OK.
If an application only makes a few RND requests then even the quality of randomness is not an issue either and the Mersenne Twister is more than adequate.
If an application only makes a few RND requests then even the quality of randomness is not an issue either and the Mersenne Twister is more than adequate.
Re: How to Manage a Critical Section of the code of a Thread in FB
@fxm, I tried to follow your thread pooling code, but it looks so complicated, so I made my own.
In the example above, all 'work' is sleeping for 3 seconds. This is repeated 'maxRuns' times per thread.
For the actual planned use, the work is variable in time. Each time a thread is done and idle, it will be given a new task until no more tasks are available. Actually, the same task, but for different data. Then we wait for all threads to finish.
Does this make any sense or am I approaching this too simplistic?
Code: Select all
#include "string.bi"
const C_GN = 10, C_RD = 12, C_YL = 14, C_WH = 15
const THR_STAT_BUSY = 0
const THR_STAT_IDLE = 1
const THR_STAT_FINISHED = 2
dim as string thrStatusStr(...) = {"Busy", "idle", "finished"}
type sub_thread_type
dim as any ptr handle 'thread handle
dim as integer status = THR_STAT_IDLE
dim as integer runCount = 0
declare sub lauch()
end type
sub sub_thread_type.lauch()
while true
select case status
case THR_STAT_BUSY
sleep 3000, 1 'hard work
runCount += 1
status = THR_STAT_IDLE
case THR_STAT_IDLE
sleep 10, 1 'wait for new task or finish command
case THR_STAT_FINISHED
exit while
end select
wend
end sub
const MAX_THREADS = 4 'is number of (sub)threads
dim as sub_thread_type subThread(MAX_THREADS - 1)
'prepare threads -> idle
print "Launching threads... ";
dim as double t0 = timer
for i as integer = 0 to MAX_THREADS-1
subThread(i).handle = threadCreate(cptr(any ptr, @sub_thread_type.lauch), @subThread(i))
sleep 1,1
next
print "ok"
dim as integer maxRuns = 5
dim as integer doneCount
do
doneCount = 0
locate 2,1
for i as integer = 0 to ubound(subThread)
select case subThread(i).status
case THR_STAT_BUSY
color C_WH, 0
'do nothing (wait)
case THR_STAT_IDLE
color C_YL, 0
if subThread(i).runCount < maxRuns then
subThread(i).status = THR_STAT_BUSY 'restart work
else
subThread(i).status = THR_STAT_FINISHED
end if
case THR_STAT_FINISHED
color C_GN, 0
doneCount += 1
case else
color C_RD, 0 'not possible
end select
print "thread(" & i & "): " & thrStatusStr(subThread(i).status) & " - " & subThread(i).runCount
next
sleep 100
loop while doneCount < MAX_THREADS
print "Clean up threads... ";
'clean up all threads
for i as integer = 0 to ubound(subThread)
threadWait(subThread(i).handle)
next
print "ok"
dim as double t1 = timer
print "End: " & format(t1 - t0, "#.000") & "s"
getkey()
end
For the actual planned use, the work is variable in time. Each time a thread is done and idle, it will be given a new task until no more tasks are available. Actually, the same task, but for different data. Then we wait for all threads to finish.
Does this make any sense or am I approaching this too simplistic?
Re: How to Manage a Critical Section of the code of a Thread in FB
Summary for my 'ThreadPooling' and 'ThreadDispatching' features (see https://www.freebasic.net/forum/viewtop ... 12#p279512):
In your version above, the user tasks to be executed and their number are hard-coded at the sub-thread level.
My version provides the user with an abstraction of everything that is sub-thread control commands with their real times:
- Only two main methods for each feature: 'PoolingSubmit()' / 'DispatchingSubmit()', and 'PoolingWait()' / 'DispatchingWait()'.
- Everything else is handled internally by each feature.
- 'TreadPooling' feature:
- The interest of my 'TreadPooling' feature is to manage a buffer queue of 'n' arbitrary user procedures to be executed by '1' internal sub-thread without having to restart (recreate) a sub-thread for executing each user procedure (only '1' internal sub-tread created in total).
- The user only inserts his procedures into a buffer queue, independently of the rate at which they will actually be executed by the internal sub-thread.
- 'TreadDispatching' feature:
- The interest of my 'TreadDispatching' feature (an extension of 'ThreadPooling' feature) is to manage a buffer queue of 'n' arbitrary user procedures to be executed by 'p' internal sub-threads without having to restart (recreate) a sub-thread (among the 'p') for executing each user procedure (only 'p' internal sub-threads created in total).
- The user only inserts his procedures into a single buffer queue (common to all sub-threads), regardless of the rate at which and by whom they will actually be executed among the internal sub-threads.
In your version above, the user tasks to be executed and their number are hard-coded at the sub-thread level.
My version provides the user with an abstraction of everything that is sub-thread control commands with their real times:
- Only two main methods for each feature: 'PoolingSubmit()' / 'DispatchingSubmit()', and 'PoolingWait()' / 'DispatchingWait()'.
- Everything else is handled internally by each feature.