我正在尝试同步一对非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对象,该对象被发布到渲染线程的处理程序中。在伪代码中,runnable看起来像这样:
{
//Swap the buffers
}
gameThread.notifyThatBuffersSwapped();
{
//Render items to screen
}
gameThread.notifyThatItemsRendered();
我的问题是实施问题。我熟悉处理程序,同步块和ReentrantLocks的概念。我知道Lock.await()Lock.signal()方法,但我发现文档在尝试理解它们在迭代循环中调用时的行为方面是不够的。
如何实现ReentrantLocks使两个线程以这种方式相互等待?如果可能,请在答案中加入实用的习语。
答案 0 :(得分:1)
我不确定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
)或跳过渲染。 (你可以通过花哨和三重缓冲来提高吞吐量,但这会增加内存使用和延迟。)