我正在尝试同步一对非 UI 线程,一个线程运行游戏逻辑,一个线程渲染,以便以逻辑高效的顺序执行任务。我自己施加的一个约束是整个系统在无分配稳定状态下运行,因此所有显示对象都被返回并“回收”,因此这两个线程必须保持一种双向对话,当我调用'swapBuffers()' 方法。
在伪代码中,游戏线程中的事件顺序如下所示:
while(condition)
{
processUserInput();
runGameLogicCycle(set number of times);
waitForBuffersSwapped();
unloadRecycleBuffer(); //This function modifies one buffer
loadDisplayBuffer(); ///This function modifies another buffer
waitForRenderFinished();
renderThread.postTask(swapBuffersAndRender);
}
选择渲染线程来执行交换缓冲区的任务,这样游戏逻辑线程就可以同时执行不修改缓冲区的任务。在我的代码中,我结合了交换缓冲区和渲染的任务,并将其定义为一个 Runnable 对象,该对象被发布到渲染线程的处理程序。在伪代码中,可运行的看起来像这样:
{
//Swap the buffers
}
gameThread.notifyThatBuffersSwapped();
{
//Render items to screen
}
gameThread.notifyThatItemsRendered();
我的问题是实现问题。我熟悉处理程序、同步块(synchronized block)和 ReentrantLocks 的概念。我知道 Lock.await() Lock.signal() 方法,但是当我试图理解它们在迭代循环中调用时的行为时,我发现文档不够充分。
如何实现 ReentrantLocks 让两个线程以这种方式相互等待?如果可能,请在您的回答中包含一个实用的成语。
最佳答案
我不确定 Lock 是否是您想要的。您确实希望当前线程拥有对对象的独占访问权,但是一旦您释放了锁,您希望确保在您再次获得锁之前另一个线程可以执行。
例如,您可以使用命名不当的 ConditionVariable :
loop:
game thread: does stuff w/objects
game thread: cv1.close()
game thread: cv2.open()
[render thread now "owns" the objects]
game thread: does stuff w/o objects
game thread: cv1.block()
game thread: [blocks]
loop:
render thread: does stuff w/objects
render thread: cv2.close()
render thread: cv1.open()
[game thread now "owns" the objects]
render thread: cv2.block()
render thread: [blocks]
这两个线程同步运行。在对非共享对象执行操作时,您会获得一些并发性,但仅此而已。
java.util.concurrent
提供了 CountDownLatch
,但这是一个一次性对象,这违背了您避免分配的愿望。奇特的 CyclicBarrier
做得更多。
这不是一个很好的解决方案——虽然锁不是您想要的,但它们是您想要的一部分,我在这里取消了它们。不可能查看代码并轻松确定两个线程不能同时对对象进行操作。
您可能需要考虑对对象进行双缓冲。您需要两倍的内存,但同步问题更简单:每个线程本质上都有自己的数据集要处理,您唯一需要暂停的时间是游戏线程想要交换数据集时。并发性最大化。如果游戏线程延迟,渲染线程将再次绘制它拥有的内容(如果您使用的是 GLSurfaceView
)或跳过渲染。 (你可以花哨地对整个事物进行三重缓冲以提高吞吐量,但这会增加内存使用和延迟。)
关于java - Android 游戏循环中的高级并发,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16923337/