MultiThread在游戏中共享资源,需要一些想法的反馈

时间:2013-02-20 12:31:05

标签: c++ multithreading locking mutex game-engine

我最近开始制作一个游戏项目(我有点新),我开始考虑如何实现多线程来提升性能。

假设您在游戏中拥有一个单位,其位置为xy。这个位置是从互联网上一步更新的,另一个线程正在使用x,y来渲染单位的图形(它必须知道它的位置)。

现在说你在这些变量上放了一个互斥锁或sephamore(有点不确定哪一个最好用)。问题当然是渲染线程。你不能停下来等待,游戏会变得迟钝。这对于互联网线程来说不是问题。除非出现问题,否则更新游戏几分钟就无所谓了。

所以我在思考如何解决这个问题,我有了一个想法。假设您创建了两组x,y(从现在开始,只允许x使其更简单,但您明白了)。所以你有x1x2

  • 现在互联网线程只更新了x1。
  • 图形线程使用x1,然后将x2更新为其当前值。

现在这个想法。如果x1处于来自互联网线程的锁定中,则图形线程将只是说

  “嘿,我等不及了。我会继续使用x2,因为那是一个很好的近似位置。”

它会这样做,直到x1再次免费。它看起来像这样。

//Thread Graphics:
if (x1 is not locked){
    lock x1:
        use x1
    unlock x1:
    x2=x1
}else{
    use x2
}

//Thread Internet:

wait until x1 is unlocked:
lock x1:
    save data to x1
unlock x1:

现在我意识到这将占用一些额外的内存,但我认为它值得,至少如果你将这种技术的使用仅限于关键的数据部分。

所以我的问题是:你们怎么看待这个想法?也许它已经是一种常见的技术,只有我不知道它的名字。如果您对如何解决此类问题有任何其他反馈,我将不胜感激。我认为这是大多数程序员的常见问题。

5 个答案:

答案 0 :(得分:0)

我喜欢这个主意,但我很担心在游戏中预测行为的后果。在我看来,你最好集中精力确保你的互斥保护(共享)数据尽可能少地受到保护。例如,不是在绘图期间锁定整个对象,而是锁定,复制所需内容,然后解锁然后绘制。在互联网方面也是如此......等待更新,获取,锁定,更新,解锁。

答案 1 :(得分:0)

在我的游戏中,我在每个帧结束时运行一系列函数。

线程1:调用服务器的位置

线程2:在X,Y

处渲染Object的帧

线程2:检查线程1的工作。

主题2:没有工作,继续。

线程1:收到X,Y!

线程1:创建将设置最终X,Y

的工作对象

线程1:锁定线程2工作队列。

线程1:将工作对象推送到工作队列

线程1:解锁线程2工作队列。

线程2:在X,Y

处渲染Object的帧

线程2:检查线程1的工作。

线程2:找到了工作!锁定工作队列......

线程2:使用线程1工作设置对象X,Y。

线程2:删除工作对象

线程2:解锁工作队列

线程2:继续渲染循环

或者,或者,您可以等到帧渲染完成,暂停片刻,更新值,然后取消暂停渲染线程。

答案 2 :(得分:0)

使用额外的内存并不是一个坏主意。

如果在处理数据时没有锁定x1,则可以改进算法。

而不是这个

//Thread Graphics:
if (x1 is not locked){
    lock x1:
        use x1
    unlock x1:
    x2=x1
}else{
    use x2
}

使用类似的东西

//Thread Graphics:
if (x1 is not locked)
    lock x1:
        x2=x1
    unlock x1:

use x2

答案 3 :(得分:0)

实际上,复制数据几乎就是我想在某个时候尝试的(在遥远的未来)。这个想法在C ++中也很简单。

如果实现值的基本类型(DualInt,DualFloat等)并提供所有标准运算符,则可以在内部将数据存储在双元素数组中。你的getters获取元素0,而你的setter修改元素1。

诀窍在于切换它们。您需要做的就是有一个读取器/写入器锁,您的getter和setter使用读取器部件,切换器使用writer部分。您可以让多个读者阅读(实际上变老并设置新值),直到您想要切换。然后你获取锁的编写部分(它阻止新的读者并等待所有读者完成),并切换一个全局变量,指示正在读取哪个元素以及正在写入哪个元素。

没有大惊小怪,没有麻烦,也没有复制很多价值观。

答案 4 :(得分:0)

这是一种维护线程安全的好方法,如果考虑双缓冲,可以扩展。在我参与过的项目中,我们有一个线程安全的数据库,其工作原理类似:

  • 有两个数据副本,即写缓冲区和读缓冲区。
  • 每当写入数据库时​​都使用写缓冲区。
  • 从中读取时使用读缓冲区。
  • 在每帧结束时交换缓冲区,因此读缓冲区现在是写缓冲区,反之亦然。

你应该能够在渲染线程中看到双缓冲的相似之处。这使我们能够在整个项目中保持线程安全。当然,权衡的是,您将数据库中任何内容的内存使用量增加一倍,因此它不适合存储大量数据。我们最大的点击通常是渲染数据和AI数据,这些数据和AI数据仅在单个线程中使用,所以这对我们来说不是一个大问题。

编辑:我忘了提到第二个权衡:当我们更改数据库中的值时,它将在下一帧之前生效。这对我们来说不是一个大问题,我们必须确保在编写系统时牢记这一点。