ThreadCreate

FreeBASIC

ThreadCreate
 
Starts a user-defined procedure in a separate execution thread

Syntax

Declare Function ThreadCreate _
( _
ByVal procptr As Sub ( ByVal userdata As Any Ptr ), _
ByVal param As Any Ptr = 0, _
ByVal stack_size As Integer = 0 _
) As Any Ptr

Usage

result = ThreadCreate ( procptr [, [ param ] [, stack_size ] ] )

Parameters

procptr
A pointer to the Sub intended to work as a thread. The sub must have the following signature (same parameters, same calling convention) to be compatible to procptr:
Declare Sub myThread ( ByVal userdata As Any Ptr )
userdata
The Any Ptr parameter of the Sub intended to work as a thread. FreeBASIC expects this parameter to be present, it must not be omitted!
param
Any Ptr argument that will be passed to the thread Sub pointed to by procptr through its userdata parameter. For example, this can be a pointer to a structure or an array containing various information for the thread sub to work with. If param is not given, 0 (zero) will be passed to the thread sub's userdata parameter instead.
stack_size
Optional number of bytes to reserve for this thread's stack.

Return Value

ThreadCreate returns an Any Ptr handle to the thread created, or a null pointer (0) on failure.

Description

The sub pointed to by procptr is started as a thread. It will be passed the content of param, or 0 (zero) if not specified, in its userdata parameter.

The sub that was started as a thread will execute in parallel with the main part of the program. The OS achieves this by assigning it to a different processor if it exists, or by alternating between execution threads on a single processor. There is no guarantee about the order in which different threads execute, and no assumptions can be made about the order in which multiple create threads actually start executing.

Before closing, programs should wait for the termination of all launched threads by using ThreadWait. Alternatively, if it's not necessary to safely wait for a thread to finish execution, Threaddetach can be used. However, if a program exits while some threads are still active, those threads will be aborted by the system. For every thread created, programs should call either ThreadWait or Threaddetach to ensure that the system resources associated with the thread handles are released. Otherwise, there may be memory or system resource leaks.

Due to the nature of threads, no assumptions about execution order can be made. In order to exchange data between multiple threads, including a thread and the main part of the program, mutexes must be used. These mutual exclusion locks can be "owned" by a single thread while doing critical work, causing other threads to wait for their turn. See MutexCreate, MutexLock, MutexUnlock, MutexDestroy.

stack_size can be used to change the thread's stack size from the system's default. This can be useful when the program requires a big stack, for example due to lots of procedure recursion or when allocating huge strings/arrays on the stack. On some systems (Linux), the stack automatically grows beyond stack_size if more space is needed; on others (Win32), this is the fixed maximum allowed. Behavior is undefined when more stack is used than the reserved size on systems where stacks are not able to grow.

Example

'' Threading synchronization using Mutexes
'' If you comment out the lines containing "MutexLock" and "MutexUnlock",
'' the threads will not be in sync and some of the data may be printed
'' out of place.

Const MAX_THREADS = 10

Dim Shared As Any Ptr ttylock

'' Teletype unfurls some text across the screen at a given location
Sub teletype( ByRef text As String, ByVal x As Integer, ByVal y As Integer )
    ''
    '' This MutexLock makes simultaneously running threads wait for each
    '' other, so only one at a time can continue and print output.
    '' Otherwise, their Locates would interfere, since there is only one
    '' cursor.
    ''
    '' It's impossible to predict the order in which threads will arrive
    '' here and which one will be the first to acquire the lock thus
    '' causing the rest to wait.
    ''
    MutexLock ttylock

    For i As Integer = 0 To (Len(text) - 1)
        Locate x, y + i
        Print Chr(text[i])
        Sleep 25
    Next

    '' MutexUnlock releases the lock and lets other threads acquire it.
    MutexUnlock ttylock
End Sub

Sub thread( ByVal userdata As Any Ptr )
    Dim As Integer id = CInt(userdata)
    teletype "Thread (" & id & ").........", 1 + id, 1
End Sub

    '' Create a mutex to syncronize the threads
    ttylock = MutexCreate()

    '' Create child threads
    Dim As Any Ptr handles(0 To MAX_THREADS-1)
    For i As Integer = 0 To MAX_THREADS-1
        handles(i) = ThreadCreate(@thread, CPtr(Any Ptr, i))
        If handles(i) = 0 Then
            Print "Error creating thread:"; i
            Exit For
        End If
    Next

    '' This is the main thread. Now wait until all child threads have finished.
    For i As Integer = 0 To MAX_THREADS-1
        If handles(i) <> 0 Then
            ThreadWait(handles(i))
        End If
    Next

    '' Clean up when finished
    MutexDestroy(ttylock)


Sub print_dots(ByRef char As String)
    For i As Integer = 0 To 29
        Print char;
        Sleep CInt(Rnd() * 100), 1
    Next
End Sub

Sub mythread(param As Any Ptr)
    '' Work (other thread)
    print_dots("*")
End Sub

    Randomize(Timer())

    Print " main thread: ."
    Print "other thread: *"

    '' Launch another thread
    Dim As Any Ptr thread = ThreadCreate(@mythread, 0)

    '' Work (main thread)
    print_dots(".")

    '' Wait until other thread has finished, if needed
    ThreadWait(thread)
    Print
    Sleep


'' Threaded consumer/producer example using mutexes

Dim Shared As Any Ptr produced, consumed 

Sub consumer( ByVal param As Any Ptr )
    For i As Integer = 0 To 9
        MutexLock produced
        Print ", consumer gets:", i
        Sleep 500
        MutexUnlock consumed
    Next
End Sub

Sub producer( ByVal param As Any Ptr )
    For i As Integer = 0 To 9
        Print "Producer puts:", i;
        Sleep 500
        MutexUnlock produced
        MutexLock consumed
    Next i
End Sub

    Dim As Any Ptr consumer_id, producer_id

    produced = MutexCreate
    consumed = MutexCreate
    If( ( produced = 0 ) Or ( consumed = 0 ) ) Then
        Print "Error creating mutexes! Exiting..."
        End 1
    End If

    MutexLock produced
    MutexLock consumed
    consumer_id = ThreadCreate(@consumer)
    producer_id = ThreadCreate(@producer)
    If( ( producer_id = 0 ) Or ( consumer_id = 0 ) ) Then
        Print "Error creating threads! Exiting..."
        End 1
    End If

    ThreadWait consumer_id
    ThreadWait producer_id

    MutexDestroy consumed
    MutexDestroy produced

    Sleep


Dialect Differences

  • Threading is not allowed in the -lang qb dialect.

Platform Differences

  • Threadcreate is not available with the DOS version / target of FreeBASIC, because multithreading is not supported by DOS kernel nor the used extender.
  • In Linux the threads are always started in the order they are created, this can't be assumed in Win32. It's an OS, not a FreeBASIC issue.

Differences from QB

  • New to FreeBASIC

See also