我有一个经典的物理线程和图形线程问题:
假设我正在运行一个线程用于物理更新,一个线程用于呈现。
在物理线程(伪代码)中:
while(true)
{
foreach object in simulation
SomeComplicatedPhysicsIntegration( &object->modelviewmatrix);
//modelviewmatrix is a vector of 16 floats (ie. a 4x4 matrix)
}在图形线程中:
while(true)
{
foreach object in simulation
RenderObject(object->modelviewmatrix);
}从理论上讲,这不需要锁,因为一个线程只在写入矩阵,而另一个线程只在读取,而我并不那么关心陈旧的数据。
更新矩阵不是原子操作的问题,有时图形线程只读取部分更新的矩阵(即。并不是所有的16个浮标都被复制,只有一部分),这意味着矩阵的一部分来自一个物理框架,部分来自前一个框架,这反过来意味着矩阵不是更多的仿射(即。它基本上是腐败的)。
在不使用锁的情况下有什么好的方法来防止这种情况发生吗?我读到了使用双缓冲的可能实现,但我无法想象不同步线程就能工作的方式。
编辑:我想我真正想使用的是某种三重缓冲,就像它们在图形显示上使用的那样。有人知道三重缓冲算法的一个很好的表现吗?
编辑2:实际上,使用非同步的三重缓冲的不是一个好的ideea (如下面的答案所示)。物理线程可以运行多个周期,占用大量的CPU和延迟图形线程,计算帧甚至永远不会呈现在最后。
我选择了一个简单的双缓冲算法与一个单一的锁,其中物理线程只计算多达1帧前的图形线程,然后交换缓冲区。就像这样:
物理学:
while(true)
{
foreach physicstimestep
foreach object in simulation
SomeComplicatedPhysicsIntegration( &object->modelviewmatrix.WriteBuffer);
LockSemaphore()
SwapBuffers()
UnlockSemaphore()
}图形:
while(true)
{
LockSemaphore()
foreach object in simulation
RenderObject(object->modelviewmatrix.ReadBuffer);
UnlockSemaphore()
}这听起来怎么样?
发布于 2011-07-03 10:29:50
但我想不出一种不同步线程的方式。
无论您使用什么样的方案,同步线程在这里都是绝对必要的。如果没有同步,您的物理线程就有可能跑在图形线程的前面,反之亦然。您的程序(通常是一个提前时间的主线程)需要控制线程操作,而不是线程机制。
双缓冲是一种允许物理线程和图形线程并行运行的方案(例如,您有一台多CPU或多核机器)。物理线程在一个缓冲区上运行,而图形线程在另一个缓冲区上运行。请注意,这会导致图形延迟,这可能是问题,也可能不是问题。
发布于 2011-07-03 10:25:45
您可以在两个线程之间维护一个共享队列,并实现物理线程,以便它只在完全填充了该矩阵中的所有值之后才向队列添加一个矩阵。这假设物理线程在每次迭代中分配一个新的矩阵(或者更具体地说,一旦将矩阵放在队列中,这些矩阵就被视为只读矩阵)。
因此,每当您的图形线程从队列中提取一个矩阵时,它都保证完全填充,并在生成矩阵时有效地表示仿真状态。
请注意,图形线程需要能够处理队列为空进行一次或多次迭代的情况,并且可能是一个好主意--在每个队列项上加盖时间戳,这样您就可以在不使用任何正式同步技术的情况下合理地保持两个线程的同步(例如,不允许图形线程使用任何具有未来时间戳的矩阵,并且允许它在队列中跳过,如果下一个矩阵来自过去太远的话)。还请注意,无论使用什么队列,如果物理线程试图在图形线程移除某项内容的同时添加某些内容,则必须实现该队列,使其不会爆炸。
发布于 2011-07-03 10:25:32
双缓冲背后的基本要点是复制要在屏幕上呈现的数据。
如果您以某种锁定方式运行,那么您的模拟线程总是在显示线程之前呈现精确的一个帧。每个被模拟的数据都会被渲染。(同步不一定很重:一个简单的条件变量可以经常被更新,并且非常便宜地唤醒呈现线程。)
如果您在没有同步的情况下运行,您的模拟线程可能会模拟从未呈现的事件,如果呈现线程无法跟上。如果您在数据中包含一个单调增加的生成数(在每个完整的模拟周期之后更新它),那么您的呈现线程可以简单地对这两个生成号进行忙-等等 (每个数据缓冲区一个)。
一旦一个(或两者都)的生成号大于最近呈现的生成,将最新的缓冲区复制到呈现线程中,更新最近呈现的计数器,然后开始呈现。当它完成后,回到忙碌的等待。
如果您的呈现线程太快,您可能会在繁忙的等待过程中消耗大量的处理器。因此,只有当您期望定期跳过呈现某些数据,并且几乎不需要等待更多的模拟时,这才有意义。
https://stackoverflow.com/questions/6562257
复制相似问题