Xbase++ V3 supports multithreading in two distinct runtime modes: single-processor mode and multi-processor mode. The mode is selected at link time depending on the application type. This article describes the technical characteristics of each mode, when each applies, and what developers need to account for.
Characteristics:
Characteristics:
Example — parallel data processing across all logical CPU cores:
Each worker thread processes a separate chunk of
Example — microservice handling concurrent HTTP requests:
The MSA framework dispatches each incoming request to a separate thread. Multiple instances of TodoEditHandler() can execute simultaneously on different cores.
In multi-processor mode, bugs may be timing-dependent and not reproducible on every run. Race conditions, deadlocks, and stale reads of shared state become possible. Adequate synchronization must be in place before enabling or targeting multi-processor mode.
Single-Processor Mode (Multithreaded/Single-Processor)
In single-processor mode, the application runs multiple threads, but only one thread executes at any given moment. The operating system schedules threads, but they never run in parallel across CPU cores.Characteristics:
- Multiple threads exist concurrently but execute serially
- Execution is deterministic and predictable
- Traditional synchronization primitives are sufficient for thread coordination
- Simpler to reason about and easier to debug
Multi-Processor Mode (Multithreaded/Multi-Processor)
In multi-processor mode, multiple threads execute simultaneously on separate CPU cores. This enables true parallel execution and higher CPU utilization.Characteristics:
- Threads run concurrently on different cores
- Execution order is non-deterministic
- Requires careful synchronization for any shared state
- Suitable for high-throughput workloads
Example — parallel data processing across all logical CPU cores:
Xbase++:
aWorkers := {}
FOR i := 1 TO GetLogicalProcessorCount()
oWorker := Thread():New()
oWorker:Start({ |nId| ProcessDataChunk(nId, aDataSet) }, i)
aadd(aWorkers, oWorker)
NEXT
ThreadWaitAll(aWorkers)
Each worker thread processes a separate chunk of
aDataSet simultaneously. The total execution time approaches 1 / N of single-threaded time where N is the number of cores, provided there is no contention on shared resources.Example — microservice handling concurrent HTTP requests:
Xbase++:
CLASS TodoEditHandler FROM RestHandler
EXPORTED:
CLASS METHOD onRegister()
METHOD getTodoItemById()
ENDCLASS
CLASS METHOD TodoEditHandler:onRegister(oEP)
::addType( "id", "N" )
oEP:map( "GET", "/todoitems/::id", "/TodoEditHandler/getTodoItemById" )
RETURN SELF
METHOD TodoEditHandler:getTodoItemById(nId)
LOCAL oTodoItem
LOCAL oResult
LOCAL oTDM
IF nId == 0
oTDM := TodoDataManager():open()
oTodoItem := oTDM:getDefault()
oTDM:close()
ELSE
oTodoItem := ::fetchTodoItemById(nId)
IF IsNull(oTodoItem)
::setError(404, "Requested todo item not found")
RETURN
ENDIF
ENDIF
RETURN oTodoItem
The MSA framework dispatches each incoming request to a separate thread. Multiple instances of TodoEditHandler() can execute simultaneously on different cores.
Mode Comparison
| Aspect | Single-Processor | Multi-Processor |
|---|---|---|
| Execution | One thread at a time | Multiple threads simultaneously |
| CPU utilization | One core | All available cores |
| Execution order | Deterministic | Non-deterministic |
| Synchronization complexity | Low | Higher |
| Typical application type | Desktop / interactive | Server / parallel processing |
| Scalability | Limited by single core | Scales with core count |
Debugging Considerations
Single-processor mode is considerably easier to debug because execution is serialized. A breakpoint in one thread pauses all observable execution, and thread interleaving is not a factor.In multi-processor mode, bugs may be timing-dependent and not reproducible on every run. Race conditions, deadlocks, and stale reads of shared state become possible. Adequate synchronization must be in place before enabling or targeting multi-processor mode.