Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/370.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 处理后台消费者/生产者任务的设计或模式 背景:_Java_Opengl_Lwjgl - Fatal编程技术网

Java 处理后台消费者/生产者任务的设计或模式 背景:

Java 处理后台消费者/生产者任务的设计或模式 背景:,java,opengl,lwjgl,Java,Opengl,Lwjgl,我有一个使用LWJGL实现的3D引擎,主要用于渲染大型“世界”——地形、植被、动画生物、天气、昼夜循环等。该引擎管理渲染所需的资源,如各种纹理、网格、模型等 当玩家在这个世界上导航时,引擎缓存各种类型的数据,丢弃/释放不再需要的资源,并按需加载/缓存新需要的数据。这由一组后台线程处理,这些线程执行I/O任务加载图像、加载新地形“块”、构建模型网格等,以及在OpenGL上下文上执行的任务队列分配新纹理、将数据上载到VBO、删除纹理等 当前设计: 除了具有依赖关系的任务外,这一切都可以很好地工作。我

我有一个使用LWJGL实现的3D引擎,主要用于渲染大型“世界”——地形、植被、动画生物、天气、昼夜循环等。该引擎管理渲染所需的资源,如各种纹理、网格、模型等

当玩家在这个世界上导航时,引擎缓存各种类型的数据,丢弃/释放不再需要的资源,并按需加载/缓存新需要的数据。这由一组后台线程处理,这些线程执行I/O任务加载图像、加载新地形“块”、构建模型网格等,以及在OpenGL上下文上执行的任务队列分配新纹理、将数据上载到VBO、删除纹理等

当前设计: 除了具有依赖关系的任务外,这一切都可以很好地工作。我将用一个例子来说明——假设引擎需要对场景中的对象应用新纹理

这包括以下步骤: 从文件系统I/O后台任务加载纹理图像 在GPU OpenGL上下文任务上分配纹理 将图像上载到纹理OpengL 将纹理附加到对象OpenGL的材质上,这并不是真的,但可以避免渲染问题 这里有几个问题需要解决: 理想情况下,我希望这些步骤或任务是原子的且可重用的,例如,引擎需要加载系统其他几个部分的图像-代码完全相同,因此可以并且应该被封装和重用。 其中一些可以并行运行,例如加载映像和分配 纹理,其中一些必须是连续的,例如无法上载 直到我们加载它并分配纹理。 某些步骤对以前的步骤具有“资源依赖性”,例如 上载步骤需要分配的纹理ID和 形象 前两个结果是相对直接的,最后一个是我正在努力解决的问题——我似乎无法想出一个像样的设计,当存在任务间依赖时,允许可重用和相对原子化的任务链接在一起

一些伪代码: TaskManager中的add方法在后台的相关队列上注册任务。每项任务完成后,经理都会收到通知。完成所有当前任务后,loadImage和allocate在这种情况下,管理器将启动序列中的下一个任务

请注意,上传任务的最后一个add调用告诉管理器它依赖于加载和分配任务

问题是: 这个问题是“生产者”任务(例如LoadImageTask)返回的“资源”如何传递给“消费者”任务(例如UploadTextureTask),而这些类没有以任何方式耦合

一种垃圾方法是,在任务完成时使用一些自定义代码调用loadImage.getImage,并使用uploadTask.setImage将其传递到“链”上,但如果必须为每个用例编写大致相同的任务管理代码,这几乎使整个方法毫无意义

我曾尝试使用通用的getter/setter方法定义使用者和生产者接口,并链接具有正确匹配数据类型的依赖任务,但如果一个任务像上面的上载任务那样消耗多个资源,这种方法就会失败

我还考虑过使用Future和Callable后台任务队列使用执行器线程池,但它们实际上是面向阻塞整个线程,而不是资源管理本身。此系统中的任务数可以达到数千,其中大多数是当前正在运行或本身已排队的挂起任务。阻止尚未启动的任务似乎毫无意义。另外,知道未来何时完成的唯一方法是调用isDone,这意味着轮询已完成任务的另一层复杂性

我在这里以及OpenGL开发者和游戏网站上研究过这个问题,但没有发现任何好的设计——它们似乎只是由剪切和粘贴的代码组成,或者是某种共享状态,通常是一张到处都有令人讨厌的投射的地图。也许我没有寻找正确的术语?或者我的整个方法都是垃圾


有没有人实施过或遇到过类似的事情?欢迎任何建议、批评和指点。为wall-o-text表示歉意,Java 8中的一些新功能似乎正好解决了这类问题,例如CompletableFuture,它使用执行器、可运行任务等与上述现有设计很好地集成。

您考虑过使用Java的Future类吗。它表示异步计算的未来结果。例如,另一个线程,因此在您的示例步骤3中,可以在构造函数中获取两个未来的对象,每个对象都计算其依赖关系。它可以给未来打电话。上车吧
se,它将阻塞,直到每个依赖项都有一个结果,然后将继续执行。我考虑过未来,但不考虑它,因为它阻塞线程而不是任务。任何时候的任务数量都可以达到数千个,其中大部分都挂起在正在执行或排队的任务上——在这种情况下,线程无论多么便宜似乎都是多余的。不过,我会再看一看未来,如果不是实际的实现,也许我可以借用一些概念。Scala有能力组合其未来的对象,因此您只需要在需要使用完成的结果时进行阻止。它与java具有良好的互操作性,因此可能有助于降低解决方案的复杂性。概念方法使一个简单的功能问题明显复杂,我看不出在同步这些任务方面有任何困难,或者我遗漏了什么…@j-p当你说你看不到“同步”这些任务有任何困难时,你指的是什么方法?也许使用我试图描述的上传任务场景作为示例? interface Task implements Runnable { TaskQueue getQueue(); } // Generic load-an-image task class LoadImageTask implements Task { private final String path; private BufferedImage image; public LoadImageTask( String path ) { this.path = path; } TaskQueue getQueue() { return TaskQueue.BACKGROUND; } public void run() { // load the image from the given location } } // Uploads an image to a given texture class UploadTextureTask implements Task { private BufferedImage image; private Texture texture; ... TaskQueue getQueue() { return TaskQueue.RENDER_THREAD; } public void run() { texture.buffer( image ); } } // Example task manager for the scenario outlined above class Example extends TaskManager { ... // Load the texture image final LoadImageTask loadImage = new LoadImageTask( ... ); add( loadImage ); // Allocate a texture final AllocateTextureTask allocate = ... add( allocate ); // Upload texture image final UploadTextureTask upload = ... add( upload, loadImage, allocate ); }