Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.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

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/git/21.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
C++ 不带锁的独立物理线程_C++_Multithreading - Fatal编程技术网

C++ 不带锁的独立物理线程

C++ 不带锁的独立物理线程,c++,multithreading,C++,Multithreading,我有一个典型的物理线程与图形线程问题: 假设我运行一个线程用于物理更新,一个线程用于渲染 在物理线程(伪代码)中: 在图形线程中: while(true) { foreach object in simulation RenderObject(object->modelviewmatrix); } 现在理论上这不需要锁,因为一个线程只对矩阵进行写入,另一个线程只进行读取,我不太关心过时的数据 问题在于更新矩阵不是一个原子操作,有时图形线程将只读取部分更新的矩阵(即,并非所有1

我有一个典型的物理线程与图形线程问题:

假设我运行一个线程用于物理更新,一个线程用于渲染

在物理线程(伪代码)中:

在图形线程中:

while(true)
{
  foreach object in simulation
    RenderObject(object->modelviewmatrix);
}
现在理论上这不需要锁,因为一个线程只对矩阵进行写入,另一个线程只进行读取,我不太关心过时的数据

问题在于更新矩阵不是一个原子操作,有时图形线程将只读取部分更新的矩阵(即,并非所有16个浮点都已复制,只是其中的一部分),这意味着矩阵的一部分来自一个物理帧,另一部分来自前一帧,这反过来意味着矩阵不再是仿射矩阵(也就是说,它基本上已经损坏了)

有没有什么好方法可以在不使用锁的情况下防止这种情况发生?我读过一篇关于使用双缓冲的可能实现的文章,但是我无法想象一种不同步线程的方法

编辑:我想我真正想使用的是图形显示器上使用的某种三重缓冲。有谁知道三重缓冲算法的好介绍吗

编辑2:事实上,使用非同步三重缓冲不是一个好主意(如下面的答案所示)。物理线程可以运行多个周期,消耗大量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()
    }

听起来怎么样?

双缓冲背后的基本要点是复制要在屏幕上呈现的数据

如果运行时带有某种锁定,那么模拟线程将始终在显示线程前一帧渲染。得到模拟的每一段数据都会被渲染。(同步不必非常繁重:可以频繁更新一个简单的条件变量,并以相当低的成本唤醒渲染线程。)

如果在没有同步的情况下运行,则如果渲染线程无法跟上,则模拟线程可能会模拟从未渲染过的事件。如果在数据中包含单调递增的生成数(在每个完整的模拟周期后更新),则渲染线程只需在这两个生成数上运行即可(每个数据缓冲区一个)

一旦一个(或两个)代数大于最近渲染的代数,将最新的缓冲区复制到渲染线程中,更新最近渲染的计数器,然后开始渲染。完成后,返回“忙等待”


如果渲染线程太快,您可能会在繁忙的等待中消耗大量处理器。因此,只有在您希望定期跳过渲染某些数据并且几乎不需要等待更多模拟的情况下,这才有意义。

您可以在两个线程之间维护一个共享队列,并实现物理线程,使其仅在在队列完全填充矩阵中的所有值后,将矩阵分配给队列。这假设物理线程在每次迭代时分配一个新矩阵(或者更具体地说,矩阵放置在队列中后将被视为只读矩阵)

因此,每当图形线程将矩阵从队列中拉出时,它都会被保证完全填充,并且在生成矩阵时,它是模拟状态的有效表示形式


请注意,图形线程将需要能够处理队列在一次或多次迭代中为空的情况,最好在每个队列条目上加上世界时间戳,这样您就可以在不使用任何正式同步技术的情况下合理地保持两个线程的同步(例如,不允许图形线程使用任何具有未来时间戳的矩阵,如果下一个矩阵在过去太远,则允许它在队列中向前跳过)。还请注意,必须实现您使用的任何队列,以便在图形线程正在删除某些内容的同时,如果物理线程尝试添加某些内容,该队列不会爆炸。

是否更新物理线程中的矩阵

获取一个块(可能是您刚刚渲染的一行),并将其位置/大小/任意值排队给物理线程。将modelviewmatrix的行反转/转置/whateverCleverMatrixStuff到另一个新行。将其发回渲染线程。在渲染中的某个适当时间复制新行。也许您不需要复制它-也许您可以将“旧”向量替换为新的还是旧的

这是可能的,还是矩阵的结构/操作/任何东西都太复杂了

所有这些都取决于数据的结构,因此此解决方案可能不合适/不可能

Rgds, 马丁

但是我无法想象一种不同步线程的方式

无论您使用的是哪种方案,同步线程在这里都是绝对必要的。如果不同步,您的物理线程可能会远远领先于图形线程,反之亦然。您的程序(通常是一个可以加快时间的主线程)需要控制线程操作,而不是线程旋转机构

双缓冲是一种允许物理线程和图形线程并行运行的方案(例如,您有一台多CPU或多核计算机)。物理线程在一个缓冲区上运行,而图形线程在另一个缓冲区上运行。请注意,这会导致图形延迟,这可能是问题,也可能不是问题

现在理论上
 while(true)
    {
     LockSemaphore()
      foreach object in simulation
        RenderObject(object->modelviewmatrix.ReadBuffer);
     UnlockSemaphore()
    }
/* pseudocode */
while (true) foreach (object in simulation) {
    auto new_object = object;
    SomeComplicatedPhysicsIntegrationInPlace(new_object)
    atomic_swap(object, new_object); // In pseudocode, ignore return value since nowhere
                                     // else changes value of object. In code, use assert, etc
}