Files
GacUI/.github/KnowledgeBase/KB_VlppOS_MultiThreading.md
T
2026-06-16 18:06:22 -07:00

4.7 KiB

Multi-threading

Cross-platform threading primitives and synchronization mechanisms for concurrent programming.

Thread Pool Operations

Use ThreadPoolLite::Queue and ThreadPoolLite::QueueLambda for thread pool execution.

Use static functions ThreadPoolLite::Queue or ThreadPoolLite::QueueLambda to run a function in another thread.

Use TaskQueue when work must be queued from multiple threads but executed by one blocking task loop.

ThreadPoolLite Benefits

The thread pool provides several advantages over manual thread creation:

  • Automatic thread lifecycle management
  • Resource pooling to avoid thread creation overhead
  • Better system resource utilization
  • Simplified concurrent programming model

Usage Examples

// Queue a function pointer
ThreadPoolLite::Queue(someFunction);

// Queue a lambda expression
ThreadPoolLite::QueueLambda([]() {
    // Work to be done in background thread
});

Task Queue Operations

TaskQueue owns a FIFO task list and a semaphore-backed blocking loop. Call QueueTask from any thread, run RunTaskQueue on the thread that should execute the work, and call QueueExitTask to leave the loop after already queued tasks are complete.

auto queue = Ptr(new TaskQueue);
auto thread = Ptr(Thread::CreateAndStart([=]()
{
    queue->RunTaskQueue();
}, false));

queue->QueueTask([]()
{
    // Runs on the task queue thread
});
queue->QueueExitTask();

Use TaskQueue instead of ThreadPoolLite when the order and single-threaded execution context of tasks matters.

Thread Control Operations

Thread Pausing

Use Thread::Sleep for thread pausing.

Use static function Thread::Sleep to pause the current thread for some milliseconds.

Thread Identification

Use Thread::GetCurrentThreadId for thread identification.

Use static function Thread::GetCurrentThreadId to get an identifier for the OS native thread running the current function.

Manual Thread Creation

Use Thread::CreateAndStart only when thread pool is insufficient.

Thread::CreateAndStart could be used to run a function in another thread while returning a Thread* to control it, but this is not recommended. Always use ThreadPoolLite if possible. Use TaskQueue when a long-lived single-threaded task loop is required.

When to Use Manual Threads

Manual thread creation should only be considered when:

  • You need fine-grained control over thread lifecycle
  • Thread-specific configurations are required
  • The thread pool doesn't meet specific timing requirements
  • Working with legacy code that requires direct thread handles

Thread Pool with Synchronization

A ThreadPoolLite call with an EventObject is a better version of Thread::Wait. TaskQueue is the alternative when multiple callers should enqueue work to one owner thread.

This approach provides better resource management and avoids the complexities of manual thread synchronization.

Extra Content

Threading Best Practices

  1. Prefer Thread Pool: Use ThreadPoolLite for most concurrent operations, and use TaskQueue for serialized owner-thread work
  2. Avoid Thread Creation: Manual thread creation adds overhead and complexity
  3. Use Synchronization Primitives: Combine threading with proper synchronization objects
  4. Handle Exceptions: Ensure proper exception handling in threaded code

Performance Considerations

  • Thread Pool: Minimal overhead for task scheduling and execution
  • Thread Creation: Significant overhead for creating and destroying threads
  • Context Switching: Consider the cost of frequent context switches
  • Resource Contention: Be aware of shared resource access patterns

Cross-Platform Considerations

The threading API provides a unified interface across platforms:

  • Windows: Uses Windows thread API internally
  • Linux: Uses pthread API internally
  • Behavior remains consistent across platforms
  • Thread IDs are platform-specific but the API is uniform

Memory Management

  • Thread pool automatically manages worker threads
  • Manual threads require explicit cleanup
  • Lambda captures should be carefully managed for thread safety
  • Avoid capturing references to stack variables in threaded lambdas

Error Handling

Threading operations can fail due to:

  • System resource exhaustion
  • Permission restrictions
  • Platform-specific limitations

Always check return values and handle potential failures gracefully.

Integration with Synchronization

Multi-threading works best when combined with appropriate synchronization primitives:

  • Use with Mutex for cross-process synchronization
  • Combine with CriticalSection for in-process protection
  • Integrate with EventObject for thread coordination
  • Apply ConditionVariable for complex waiting scenarios