I am currently working on a minecraft clone, and I am currently trying to implement multithreading which I have never worked with (I've read some of the docs by Microsoft but that's it) to generate chunks without making the game hang for a second.
My World
class stores a Dictionary (System.Collections.Generic
) of Chunks which each store a 3d array of block ids. In my Update
method I add the new chunks to be generated to a queue (working as intended) and I have a different thread that is supposed to work down the queue and generate the chunks one by one:
(In World
class:)
Dictionary<Vector2, Chunk> chunks = new Dictionary<Vector2, Chunk>();
Queue<Vector2> chunksToCreate = new Queue<Vector2>();
public World(Chunk.ChunkGen generator, int program, int seed) {
generatorFunc = generator;
Program = program;
Seed = seed;
chunksCreator = new Thread(CreateChunks);
chunksCreator.Start();
}
private void CreateChunks() {
while (true) {
if (chunksToCreate.Count == 0)
continue;
Vector2 vec = chunksToCreate.First();
chunks[vec] = generatorFunc(this, (int)vec.X, (int)vec.Y);
chunksToCreate.TryDequeue(out Vector2 outVec);
}
}
My problem is that, when I create chunks simply by calling the generatorFunc (delegate) they are added to the dictionary and rendered by opentk as expected, but when I create with this other thread, they also get added to the dictionary and in the VS Debugger everything inside the objects looks correct, BUT they aren't rendered at all.
I have already tried using ConcurrentQueue
and ConcurrentDictionary
but with no changes.
I'd really appreciate it if someone would help me with this as I am completely new to all this multithreading stuff and all the articles and books seem really overwhelming.
The UI does not like graphics objects created in other threads. E.g. if your chunks contain brushes, they might not work in the UI thread if they were created in a working thread.
In your case it makes no difference whether you are using the normal or the thread-safe collections, because you created only one thread. To experience a speed gain would have to create and run several threads. Using thread-safe collections would be crucial in that case and the CreateChunks
method would have to look like
private void CreateChunks() {
while (chunksToCreate.TryDequeue(out Vector2 vec)) {
chunks.TryAdd(vec, generatorFunc(this, (int)vec.X, (int)vec.Y));
}
}
But creating the right number of threads and managing them yourself can be avoided by using the Task Parallel Library (TPL).
Parallel.ForEach(chunksToCreate, vec =>
chunks.TryAdd(vec, generatorFunc(this, (int)vec.X, (int)vec.Y))
);
Note that the source chunksToCreate
needs not be thread safe, since Parallel.ForEach
assigns the items to the threads before starting them; however, the dictionary chunks
must, as it will be accessed by multiple threads in parallel.
ConcurrentDictionary<Vector2, Chunk> chunks = new ConcurrentDictionary<Vector2, Chunk>();
List<Vector2> chunksToCreate = new List<Vector2>();
public World(Chunk.ChunkGen generator, int program, int seed) {
Program = program;
Seed = seed;
Parallel.ForEach(chunksToCreate, vec =>
chunks.TryAdd(vec, generator(this, (int)vec.X, (int)vec.Y))
);
}