gpt4 book ai didi

java - 处理后台消费者/生产者任务的设计或模式

转载 作者:太空宇宙 更新时间:2023-11-04 14:52:58 24 4
gpt4 key购买 nike

背景:

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

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

当前设计:

除了具有依赖项的任务以外,所有这些都很好地工作。我将通过一个示例进行说明-假设引擎需要将新纹理应用于场景中的对象。

这包括以下步骤:


从文件系统加载纹理图像(I / O后台任务)
在GPU上分配纹理(OpenGL上下文任务)
将图像上传到纹理(OpengL)
将纹理附加到对象的材质上(OpenGL,不是真的,但是避免了渲染问题)


这里有一些问题需要解决:


理想情况下,我希望这些步骤(或任务)是原子性的并且可重复使用,例如,引擎需要为系统的其他几个部分加载图像-代码完全相同,因此可以(并且应该)封装并重新使用-用过的。
其中一些可以并行运行(例如,加载图像并分配
纹理),其中一些必须是连续的,例如无法上传
图片,直到我们加载它并分配了纹理。
有些步骤与先前的步骤具有“资源依赖性”,例如在
特别是上传步骤需要分配的纹理ID和
图片。


前两个结果相对简单明了,最后一个是我正在努力解决的问题-我似乎无法想出一个像样的设计来允许将可重用和相对原子的任务在相互关联时链接在一起-任务依赖关系。

一些伪代码:

界面Task实现了Runnable {
    TaskQueue getQueue();
}

//通用的加载图片任务
LoadImageTask类实现了任务{
    私有最终String路径;
    私有BufferedImage图像;

    公共LoadImageTask(字符串路径){
        this.path =路径;
    }

    TaskQueue getQueue(){返回TaskQueue.BACKGROUND; }

    公共无效run(){
        //从给定位置加载图像
    }
}

//将图像上传到给定的纹理
类UploadTextureTask实现任务{
    私有BufferedImage图像;
    私有Texture纹理;

    ...

    TaskQueue getQueue(){返回TaskQueue.RENDER_THREAD; }

    公共无效run(){
        texture.buffer(image);
    }
}

//上面概述的方案的示例任务管理器
类示例扩展TaskManager {
    ...

    //加载纹理图像
    最后的LoadImageTask loadImage = new LoadImageTask(...);
    add(loadImage);

    //分配纹理
    最后的AllocateTextureTask分配= ...
    添加(分配);

    //上传纹理图片
    最终UploadTextureTask upload = ...
    添加(上传,loadImage,分配);
}


add中的TaskManager方法在后台将任务注册到相关队列中。当每个任务完成时,经理会收到通知。当所有当前任务完成时(在这种情况下为loadImageallocate),管理器将启动序列中的下一个任务。

请注意,对add任务的最后一个upload调用告诉管理员,它依赖于loadallocate任务。

问题:

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

一种(垃圾)方式是拥有一些自定义代码,该代码在任务完成时调用loadImage.getImage()并将其使用uploadTask.setImage()传递给“链”,但是如果必须粗略地编写该代码,这几乎使整个方法变得毫无意义。每个用例都使用相同的任务管理代码。

我尝试使用通用的getter / setter方法定义消费者和生产者接口,并链接具有正确匹配的数据类型的从属任务,但是如果一个任务消耗了多个资源(如上载任务所做的那样),则这种方法就会失效。

我还考虑过使用FutureCallable(后台任务队列始终使用Executor线程池),但它们的使用方向实际上是阻塞整个线程,而不是资源管理本身。该系统中的任务数可以是数千个,其中大多数是当前正在运行或正在排队的待处理任务。阻止尚未开始的任务似乎毫无意义。知道未来何时完成的唯一方法(?)是调用isDone,这意味着轮询完成任务的另一层复杂性。

我在这里以及在OpenGL开发人员和游戏网站上都研究了这个问题,但是还没有遇到任何好的设计-它们似乎只是由剪切和粘贴代码或某种共享状态(通常是地图)组成,其中包含许多到处都是讨厌的演员。也许我没有搜索正确的术语?也许我的整个方法都是垃圾?

有没有人实施或遇到过类似的事情?任何建议,批评,指点都是欢迎的。 (对墙贴文本表示歉意)

最佳答案

Java 8中的一些新功能似乎可以完全解决此类问题,例如CompletableFuture,它使用ExecutorRunnable任务等与上述现有设计很好地集成在一起。

关于java - 处理后台消费者/生产者任务的设计或模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23542776/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com