我目前正在开发一个《我的世界》副本,并且我目前正在尝试实现从未使用过的多线程(我已经阅读了Microsoft的一些文档,仅此而已)来生成块而不会使游戏挂起一秒钟。
我的World
类存储一个Chunk的字典(System.Collections.Generic
),每个字典都存储一个3d的块ID数组。在我的Update
方法中,我将要生成的新块添加到队列中(按预期工作),并且我有一个不同的线程应该在队列中工作并逐个生成块:
(在World
类中:)
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);
}
}
我的问题是,当我仅通过调用generatorFunc(delegate)创建块时,会将它们添加到字典中,并按预期由opentk呈现,但是当我使用另一个线程创建时,它们也会被添加到字典中,并且在VS Debugger中,对象内部的所有内容看起来都是正确的,但它们根本没有呈现。
我已经尝试使用ConcurrentQueue
和ConcurrentDictionary
,但没有任何更改。
如果有人能为我提供帮助,我将非常感激,因为我对所有这些多线程技术完全陌生,而且所有文章和书籍似乎都让人觉得不知所措。
答案 0 :(得分:2)
UI不喜欢在其他线程中创建的图形对象。例如。如果您的块包含画笔,那么如果它们是在工作线程中创建的,则它们可能无法在UI线程中工作。
在您的情况下,无论您使用普通集合还是线程安全集合都没有区别,因为您仅创建了一个线程。要体验速度提升,必须创建并运行多个线程。在这种情况下,使用线程安全的集合将至关重要,而CreateChunks
方法必须看起来像
private void CreateChunks() {
while (chunksToCreate.TryDequeue(out Vector2 vec)) {
chunks.TryAdd(vec, generatorFunc(this, (int)vec.X, (int)vec.Y));
}
}
但是使用Task Parallel Library (TPL)可以避免创建正确数量的线程并自己管理它们。
Parallel.ForEach(chunksToCreate, vec =>
chunks.TryAdd(vec, generatorFunc(this, (int)vec.X, (int)vec.Y))
);
请注意,源chunksToCreate
不必是线程安全的,因为Parallel.ForEach
会在启动线程之前将它们分配给线程。但是,必须使用字典chunks
,因为它将由多个线程并行访问。
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))
);
}