-
Notifications
You must be signed in to change notification settings - Fork 0
Core.Utilities.ThreadSafeList
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.
-
Thread Synchronization: It uses a private
object _lockto ensure that only one thread can modify the list (Add,Remove) or access theCountproperty at a time. -
Custom Locked Enumerator: This is the most distinctive part of the class.
- Standard lists throw an exception if you modify them while iterating (
foreach). -
ThreadSafeListprovides aLockedEnumeratorstruct that callsMonitor.Enterwhen created andMonitor.Exitwhen disposed (at the end of aforeachblock). - This ensures that the list cannot be modified by another thread while you are iterating through it.
- Standard lists throw an exception if you modify them while iterating (
-
Performance: The enumerator is a
struct, which means using it in aforeachloop does not cause heap allocations (GC pressure), making it suitable for game engines or high-performance scenarios.
- 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.
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}");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)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...
}
}
}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.