由不同线程添加到 Dictionary 的对象不是由 OpenTK 渲染的

Objects added to Dictionary by different thread not rendered by OpenTK

我目前正在开发一个 minecraft 克隆,我目前正在尝试实现我从未使用过的多线程(我已经阅读了 Microsoft 的一些文档,仅此而已)来生成块而不制作游戏挂一秒。

我的 World class 存储一个块字典 (System.Collections.Generic),每个块存储一个 3d 数组的块 ID。在我的 Update 方法中,我将要生成的新块添加到队列中(按预期工作),并且我有一个不同的线程应该在队列中工作并一个一个地生成块:

(在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);
    }
}

我的问题是,当我简单地通过调用 generatorFunc(委托)创建块时,它们被添加到字典中并按预期由 opentk 呈现,但是当我使用另一个线程创建时,它们也被添加到字典和 VS 调试器中对象内的所有内容看起来都是正确的,但它们根本没有呈现。

我已经尝试使用 ConcurrentQueueConcurrentDictionary 但没有任何变化。

如果有人能帮助我,我将不胜感激,因为我对所有这些多线程的东西都是全新的,而且所有的文章和书籍看起来都让人不知所措。

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))
    );
}