Skip to content

Core.Utilities.ThreadSafeList

Dennis Steffen edited this page Jan 4, 2026 · 1 revision

The file ThreadSafeList.cs defines a utility class designed to provide a thread-safe wrapper around the standard List<T>. It allows multiple threads to add or remove items simultaneously without causing data corruption or race conditions.

Key Features and Explanation

  1. Thread Synchronization: It uses a private object _lock to ensure that only one thread can modify the list (Add, Remove) or access the Count property at a time.
  2. Custom Locked Enumerator: This is the most distinctive part of the class.
    • Standard lists throw an exception if you modify them while iterating (foreach).
    • ThreadSafeList provides a LockedEnumerator struct that calls Monitor.Enter when created and Monitor.Exit when disposed (at the end of a foreach block).
    • This ensures that the list cannot be modified by another thread while you are iterating through it.
  3. Performance: The enumerator is a struct, which means using it in a foreach loop does not cause heap allocations (GC pressure), making it suitable for game engines or high-performance scenarios.

Use Cases

  • Entity Management: Storing a list of game objects where background threads might be spawning new entities while the main thread is iterating through them to render or update.
  • Logging/Event Buffering: A central list where various worker threads "post" messages or events that are later processed by a single consumer thread.
  • Resource Management: Managing a list of loaded assets or textures where multiple loading threads might register new assets simultaneously.

Examples

1. Basic Usage (Thread-Safe Operations)

Since the Add and Remove methods are locked internally, you can safely call them from any thread.

var playerNames = new ThreadSafeList<string>();

// Thread A
Task.Run(() => playerNames.Add("Player_1"));

// Thread B
Task.Run(() => playerNames.Add("Player_2"));

// Main Thread
Console.WriteLine($"Total players: {playerNames.Count}");

2. Safe Iteration with foreach

The custom enumerator ensures that no other thread can modify the list while the loop is running. This prevents the common InvalidOperationException: Collection was modified.

var bullets = new ThreadSafeList<Bullet>();

// This is safe even if bullets are being added/removed by other threads
// because the lock is held for the duration of the 'foreach'
foreach (var bullet in bullets)
{
    bullet.Update(deltaTime);
} 
// The lock is automatically released here (via Dispose)

3. Integration in a Game Loop

In a scenario like the Wall class provided in your context, you might use a ThreadSafeList to track all active walls if walls were generated or destroyed from background physics or networking tasks.

public class WallManager
{
    private ThreadSafeList<Wall> _activeWalls = new();

    public void OnWallSpawned(Wall wall)
    {
        // Thread-safe addition
        _activeWalls.Add(wall);
    }

    public void UpdateAll(float dt)
    {
        // Thread-safe iteration
        foreach (var wall in _activeWalls)
        {
            // Update logic...
        }
    }
}

Important Consideration

Because the GetEnumerator() holds a lock until the loop finishes, you should avoid heavy processing or blocking calls inside the foreach loop, as it will block any other thread trying to Add or Remove from the list until the loop completes.

Clone this wiki locally