Multi Threading - aadityap0901 - 10-17-2025
I know some of you might say - just use timers and gosub routines
but that's not multi-threading
I know qb64 is a single threaded language,
but I have an idea.
We can introduce some subroutines like the Sub _GL
example: Code: (Select All)
Sub _Thread1
End Sub
Sub _Thread2
End Sub
I just don't know how to implement this from libqb
RE: Multi Threading - DSMan195276 - 10-17-2025
It's not possible without significant changes to the runtime, the runtime is not designed to be thread-safe so there's many operations that will break if they happen across multiple threads (Ex. Using strings, creating/freeing images, reading input, etc.).
_GL gets around this by pausing execution of the main thread while the _GL thread is working, ensuring there's never two threads running your QB64 code at the same time.
RE: Multi Threading - aadityap0901 - 10-17-2025
Ok.
But, is there a way I can try that myself?
I opened the code, but I can't figure out anything. So, I turned here.
RE: Multi Threading - DSMan195276 - 10-17-2025
There's no easy way to try it because it will just blow up.
If you do want to try it you can use a C++ library to create threads via `DECLARE LIBRARY` (and probably some helper C++ code), then call back into your QB64 code from them. I'm not going to go in-depth on it though because it's not useful.
RE: Multi Threading - aadityap0901 - 10-17-2025
(10-17-2025, 02:11 PM)DSMan195276 Wrote: There's no easy way to try it because it will just blow up.
If you do want to try it you can use a C++ library to create threads via `DECLARE LIBRARY` (and probably some helper C++ code), then call back into your QB64 code from them. I'm not going to go in-depth on it though because it's not useful.
Ok
I will try the pthreads library
Thanks
Update:
Not working, because pthreads need a pointer to a function, which is inaccessible in qb64.
RE: Multi Threading - justsomeguy - 10-17-2025
Despite what others have said, I have had some success at threading in QB64.
https://qb64phoenix.com/forum/showthread.php?tid=2739
First, I have never tried it with OpenGL.
Keep in mind that you are extremely limited on what you can do. A lot of QB64 functionality will be closed off to the child threads and possibly the main thread. Subroutines and Functions will also be problematic if multiple threads are trying to access them simultaneously. Lots of pitfalls, and not much help to be had.
Don't get me wrong I'm not discouraging you. Just keep your expectations realistic.
I suggest looking over the code I posted, run the code, try to understand the code.
I'm not sure what platform you are on, but on windows I had to add '-pthread' to compiler settings and '--static' to the linker settings.
RE: Multi Threading - hsiangch_ong - 10-19-2025
quite honestly. if you want to mess around with threads. you will have to program in something else other than qb64 phoenix.
like the others said. regardless of what operating system you would want to run your program in. there will be race conditions. one service trying to step over all others. trying to be first. in setting itself up and/or having access to ram, disk space and other resources.
that's why string access is inherently not thread-safe. this is a killer. for many people who like basic's convenience with string handling. that's why a programming language cannot be used neither which employs garbage collection. such as lua. it's harmful for music-production application for example. that's why a complex program like reaper. crashes occasionally. there's just too much to keep track of. while needing to keep things in tight synchronization. if such a demanding a program had to make sure things were thread-safe. it would just come to a grinding halt. ("mutex lock" is an essential part of keeping track of thread execution.) a bit better than that. it could freeze at specific intervals. which is very irritating. many people have rejected linux distributions because of it.
in the least. in some programming system implementations i've seen. the "thread" has to be an entire function. which relies only on global variables. cannot even accept arguments. no local variables of any kind. the stack for such a function. either will not exist or will have to be protected.
the subject should be read about extensively.
RE: Multi Threading - mdijkens - 10-19-2025
As stated in the opening post you can use timers when that is enough.
Ive used sometines
Code: (Select All)
Dim thread(0 To 9) As Integer
For t% = 0 To 9: thread(t%) = _FreeTimer: Next t%
On Timer(thread(0), .01) processRequest 0
On Timer(thread(1), .01) processRequest 1
On Timer(thread(2), .01) processRequest 2
On Timer(thread(3), .01) processRequest 3
On Timer(thread(4), .01) processRequest 4
On Timer(thread(5), .01) processRequest 5
On Timer(thread(6), .01) processRequest 6
On Timer(thread(7), .01) processRequest 7
On Timer(thread(8), .01) processRequest 8
On Timer(thread(9), .01) processRequest 9
For t% = 0 To 9: Timer(thread(t%)) On: Next t%
Another option when I really wanted to run several (long running) process in parallel was to use
Code: (Select All)
Shell -DONTWAIT on several separate executables
It's not much, but it can work in some cases
RE: Multi Threading - SpriggsySpriggs - 10-22-2025
Multithreading can be done, but it is complex. Here is some code to get you started on Windows, if you still wish to do so:
threadwin.h (Size: 278 bytes / Downloads: 14)
Code: (Select All) #include<strsafe.h>
int32 FUNC_MYTHREADFUNCTION(ptrszint*_FUNC_MYTHREADFUNCTION_OFFSET_LPPARAM);
extern "C"{
__declspec(dllexport) int32 MyThreadFunction(ptrszint*lpParam){
return FUNC_MYTHREADFUNCTION((lpParam));
}
}
int32 sizeoftchar(){
return sizeof(TCHAR);
}
threading.bas (Size: 5.94 KB / Downloads: 12)
Code: (Select All)
Option _Explicit
$Console:Only
Type MyData
As Long val1, val2
End Type
Const BUF_SIZE = 255
Const MAX_THREADS = 20
Const HEAP_ZERO_MEMORY = &H00000008
Const INFINITE = 4294967295
Const STD_OUTPUT_HANDLE = -11
Const INVALID_HANDLE_VALUE = -1
Const MB_OK = 0
Const FORMAT_MESSAGE_ALLOCATE_BUFFER = &H00000100
Const FORMAT_MESSAGE_FROM_SYSTEM = &H00001000
Const FORMAT_MESSAGE_IGNORE_INSERTS = &H00000200
Const LANG_NEUTRAL = &H00
Const SUBLANG_DEFAULT = &H01
Const LMEM_ZEROINIT = &H0040
Declare CustomType Library
Function LoadLibrary%& (lpLibFileName As String)
Function GetProcAddress%& (ByVal hModule As _Offset, lpProcName As String)
Sub FreeLibrary (ByVal hLibModule As _Offset)
Function GetLastError& ()
Function HeapAlloc%& (ByVal hHeap As _Offset, Byval dwFlags As Long, Byval dwBytes As _Offset)
Function GetProcessHeap%& ()
Sub ExitProcess (ByVal uExitCode As _Unsigned Long)
Function CreateThread%& (ByVal lpThreadAttributes As _Offset, Byval dwStackSize As _Offset, Byval lpStartAddress As _Offset, Byval lpParameter As _Offset, Byval dwCreationFlags As Long, Byval lpThreadId As _Offset)
Function WaitForMultipleObjects& (ByVal nCount As Long, Byval lpHandles As _Offset, Byval bWaitAll As _Byte, Byval dwMilliseconds As Long)
Sub WaitForMultipleObjects (ByVal nCount As Long, Byval lpHandles As _Offset, Byval bWaitAll As _Byte, Byval dwMilliseconds As Long)
Sub CloseHandle (ByVal hObject As _Offset)
Sub HeapFree (ByVal hHeap As _Offset, Byval dwFlags As Long, Byval lpMem As _Offset)
Sub StringCchPrintf Alias "StringCchPrintfA" (ByVal pszDest As _Offset, Byval cchDest As _Offset, pszFormat As String, Byval arg1 As Long, Byval arg2 As Long)
Sub StringCchPrintf2 Alias "StringCchPrintfA" (ByVal pszDest As _Offset, Byval cchDest As _Offset, pszFormat As String, lpszFunction As String, Byval error As Long, Byval lpMsgBuf As _Offset)
Sub StringCchLength Alias "StringCchLengthA" (ByVal psz As _Offset, Byval cchMax As _Offset, Byval pcchLength As _Offset)
Function GetStdHandle%& (ByVal nStdHandle As Long)
Function CreateMutex%& Alias "CreateMutexA" (ByVal lpMutexAttributes As _Offset, Byval bInitialOwner As Long, Byval lpName As _Offset)
Sub WriteConsole (ByVal hConsoleOutput As _Offset, Byval lpBuffer As _Offset, Byval nNumberOfCharsToWrite As Long, Byval lpNumberOfCharsWritten As _Offset, Byval lpReserved As _Offset)
Sub FormatMessage Alias FormatMessageA (ByVal dwFlags As Long, Byval lpSource As Long, Byval dwMessageId As Long, Byval dwLanguageId As Long, Byval lpBuffer As _Offset, Byval nSize As Long, Byval Arguments As _Offset)
Sub MessageBox Alias "MessageBoxA" (ByVal hWnd As _Offset, Byval lpText As _Offset, lpCaption As String, Byval uType As _Unsigned Long)
Sub LocalFree (ByVal hMem As _Offset)
Function LocalAlloc%& (ByVal uFlags As _Unsigned Long, Byval uBytes As _Unsigned _Offset)
Function lstrlen& Alias "lstrlenA" (ByVal lpString As _Offset)
Function LocalSize%& (ByVal hMem As _Offset)
End Declare
Declare Library "threadwin"
Function sizeoftchar& ()
End Declare
Declare Library
Function MAKELANGID& (ByVal p As Long, Byval s As Long)
End Declare
Dim As _Offset libload: libload = LoadLibrary(Command$(0))
Dim As _Offset MyThreadFunc: MyThreadFunc = GetProcAddress(libload, "MyThreadFunction")
Dim As MyData pDataArray(1 To MAX_THREADS)
Dim As Long dwThreadIdArray(1 To MAX_THREADS)
Dim As _Offset hThreadArray(1 To MAX_THREADS), heap(1 To MAX_THREADS)
Dim As _Offset ghMutex: ghMutex = CreateMutex(0, 0, 0)
If ghMutex = 0 Then
ErrorHandler "CreateMutex"
End If
Dim As Long i
For i = 1 To MAX_THREADS
heap(i) = HeapAlloc(GetProcessHeap, HEAP_ZERO_MEMORY, Len(pDataArray(i)))
Dim As _MEM pdata: pdata = _MemNew(8)
_MemPut pdata, pdata.OFFSET, heap(i)
_MemGet pdata, pdata.OFFSET, pDataArray(i)
If heap(i) = 0 Then
ExitProcess 2
End If
pDataArray(i).val1 = i
pDataArray(i).val2 = i + 100
hThreadArray(i) = CreateThread(0, 0, MyThreadFunc, _Offset(pDataArray(i)), 0, _Offset(dwThreadIdArray(i)))
If hThreadArray(i) = 0 Then
ErrorHandler "CreateThread"
ExitProcess 3
End If
Next
WaitForMultipleObjects MAX_THREADS, _Offset(hThreadArray()), 1, INFINITE
For i = 1 To MAX_THREADS
CloseHandle hThreadArray(i)
If heap(i) <> 0 Then
HeapFree GetProcessHeap, 0, heap(i)
End If
Next
CloseHandle ghMutex
_MemFree pdata
FreeLibrary libload
Function MyThreadFunction& (lpParam As _Offset)
Dim As String * BUF_SIZE msgBuf
Dim As _Offset hStdout
Dim As Long cchStringSize, dwChars
Dim As MyData MyData
hStdout = GetStdHandle(STD_OUTPUT_HANDLE)
If hStdout = INVALID_HANDLE_VALUE Then
MyThreadFunction = 1
End If
Dim As _MEM PMYDATA: PMYDATA = _MemNew(8)
_MemPut PMYDATA, PMYDATA.OFFSET, lpParam
_MemGet PMYDATA, PMYDATA.OFFSET, MyData
StringCchPrintf _Offset(msgBuf), BUF_SIZE, "Parameters = %d, %d" + Chr$(10) + Chr$(0), MyData.val1, MyData.val2
StringCchLength _Offset(msgBuf), BUF_SIZE, _Offset(cchStringSize)
WriteConsole hStdout, _Offset(msgBuf), cchStringSize, _Offset(dwChars), 0
_MemFree PMYDATA
MyThreadFunction = 0
End Function
Sub ErrorHandler (lpszFunction As String)
Dim As _Offset lpMsgBuf, lpDisplayBuf
Dim As Long dw: dw = GetLastError
FormatMessage FORMAT_MESSAGE_ALLOCATE_BUFFER Or FORMAT_MESSAGE_FROM_SYSTEM Or FORMAT_MESSAGE_IGNORE_INSERTS, 0, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), _Offset(lpMsgBuf), 0, 0
lpDisplayBuf = LocalAlloc(LMEM_ZEROINIT, (lstrlen(lpMsgBuf) + lstrlen(_Offset(lpszFunction)) + 40) * sizeoftchar)
StringCchPrintf2 lpDisplayBuf, LocalSize(lpDisplayBuf) / sizeoftchar, "%s failed with error %d:" + Chr$(10) + " %s" + Chr$(0), lpszFunction + Chr$(0), dw, lpMsgBuf
MessageBox 0, lpDisplayBuf, "Error" + Chr$(0), MB_OK
LocalFree lpMsgBuf
LocalFree lpDisplayBuf
End Sub
Note: The code is a few years old at this point, I think. It is based on this MSDN threading example.
Also, you mentioned pointers to functions. I can help you with that. The code above uses pointers to QB64 functions. Once you know how to do that, it's very simple.
|