I've been trying to write a chunk loading system for a game I'm making in Godot. For that I want to use threads to load multiple chunks at once.
int threadsToUtilise = 7;
Thread[] threads; // array to store threads
ArrayMesh[] threadResults; // array to save meshes returned by WorldLoader.GetChunk();
Vector3I[] chunkIDsLoading; // array to save position (Vector3I) of the chunk thread[i] is loading; serves to later position the mesh at the right position
// the arrays are initialised with a size of threadsToUtilise (7)
List<Vector3I> chunksToLoad = new(); // List to store positions of all chunks that still need to be loaded in
public override void _Process(double delta) // method runs every frame
{
for (var i = 0; i < threadsToUtilize; i++)
{
if (threads[i] == null){
if (chunksToLoad.Count == 0) break;
threads[i] = new Thread(() => { threadResults[i] = WorldLoader.GetChunk(chunksToLoad[0], false); });
threads[i].Start();
chunkIDsLoading[i] = chunksToLoad[0];
chunksToLoad.RemoveAt(0);
}
else if (!threads[i].IsAlive) {
if (threadResults[i] != null) {
// implement some stuff
}
threads[i] = null;
threadResults[i] = null;
chunkIDsLoading[i] = Vector3I.Zero;
}
}
}
Generally I wanted to load a chunk and as soon as the mesh is created, implement it. It more or less works but there are two main problems:
This looks like a "capture" problem; try:
var idx = i;
var chunk = chunksToLoad[0];
threads[i] = new Thread(() => { threadResults[idx] = WorldLoader.GetChunk(chunk, false); });
which creates snapshots of the values that we "capture" in the lambda. Otherwise, you are capturing the variable, not the value at the time you create the lambda - when the thread runs (at some undefined point in the future), the values at that time will be used, which isn't what you intended. The captured variables idx
and chunk
are scoped by their declaration location (put simply: the nearest {
), which means you'll get a separate definition for each loop iteration.