gpt4 book ai didi

c++ - 构建和同步多线程游戏循环

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:05:12 25 4
gpt4 key购买 nike

我遇到了一个关于游戏循环线程安全的轻微难题。我下面的是 3 个线程(包括主线程),它们可以一起工作。一种用于事件管理(主线程),一种用于逻辑,一种用于渲染。所有这 3 个线程都存在于它们自己的类中,如下所示。在基本测试中,该结构可以正常工作。该系统使用 SFML 并使用 OpenGL 进行渲染。

int main(){
Gamestate gs;
EventManager em(&gs);
LogicManager lm(&gs);
Renderer renderer(&gs);

lm.start();
renderer.start();
em.eventLoop();

return 0;
}

但是,正如您可能已经注意到的,我有一个“Gamestate”类,旨在充当需要在线程之间共享的所有资源的容器(主要是将 LogicManager 作为编写器,将 Renderer 作为读取器。 EventManager 主要用于窗口事件)。我的问题是:(1 和 2 是最重要的)

1) 这是处理事情的好方法吗?意思是使用“全局”Gamestate 类是个好主意吗?有更好的方法吗?

2) 我的意图是让 Gamestate 在 getters/setters 中有互斥量,除了它对读取不起作用,因为我无法在对象仍然锁定时返回它,这意味着我必须将同步放在外面getters/setters 并公开互斥量。这也意味着我将拥有大量用于所有不同资源的互斥体。解决这个问题最优雅的方法是什么?

3) 我让所有线程访问“bool run”以检查是否继续它们的循环

while(gs->run){
....
}

如果我在 EventManager 中收到退出消息,run 将设置为 false。我是否需要同步该变量?我会把它设置为 volatile 吗?

4) 不断取消引用指针等是否会对性能产生影响?例如 gs->objects->entitylist.at(2)->move();做所有那些“->”和“。”导致任何重大放缓?

最佳答案

全局状态

1) Is this a good way of going about things? Meaning is having a "global" Gamestate class a good idea to use? Is there a better way of going about it?

对于游戏而言,相对于一些可重用的代码,我认为全局状态就足够了。您甚至可以避免传递游戏状态指针,而是真正将其设为全局变量。

同步

2) My intention was to have Gamestate have mutexes in the getters/setters, except that doesn't work for reading because I can't return the object while it's still locked, which means I'd have to put synchronization outside of the getters/setters and make the mutexes public. It also means I'd have a bloody ton of mutexes for all the different resources. What is the most elegant way of going about this problem?

我会尝试从交易的角度考虑这一点。将每个状态更改包装到它自己的互斥锁定代码中不仅会影响性能,而且如果代码获取一个状态元素,对其执行一些计算并稍后设置值,而其他代码修改了该状态,则可能会导致实际上不正确的行为之间的相同元素。因此,我尝试以这样的方式构建 LogicManagerRenderer,以便将所有与 Gamestate 的交互都捆绑在几个地方。在该交互期间,线程应在状态上持有一个互斥体。

如果你想强制使用互斥锁,那么你可以在至少有两个类的地方创建一些结构。我们称它们为 GameStateDataGameStateAccessGameStateData 将包含所有状态,但不提供对其的公共(public)访问。 GameStateAccess 将是 GameStateData 的友元,并提供对其私有(private)数据的访问。 GameStateAccess 的构造函数将获取对 GameStateData 的引用或指针,并锁定该数据的互斥量。析构函数将释放互斥体。这样,您操纵状态的代码将简单地编写为一个 block ,其中 GameStateAccess 对象在范围内。

但是仍然存在一个漏洞:如果从此 GameStateAccess 类返回的对象是指向可变对象的指针或引用,那么此设置不会阻止您的代码携带此类指针受互斥量保护的范围。为防止这种情况,请注意您的编写方式,或者使用一些自定义的类似指针的模板类,一旦 GameStateAccess 超出范围就可以将其清除,或者确保您只按值传递内容不是引用。

例子

使用C++11,上述锁管理思想可以实现如下:

class GameStateData {
private:
std::mutex _mtx;
int _val;
friend class GameStateAccess;
};
GameStateData global_state;

class GameStateAccess {
private:
GameStateData& _data;
std::lock_guard<std::mutex> _lock;
public:
GameStateAccess(GameStateData& data)
: _data(data), _lock(data._mtx) {}
int getValue() const { return _data._val; }
void setValue(int val) { _data._val = val; }
};

void LogicManager::performStateUpdate {
int valueIncrement = computeValueIncrement(); // No lock for this computation
{ GameStateAccess gs(global_state); // Lock will be held during this scope
int oldValue = gs.getValue();
int newValue = oldValue + valueIncrement;
gs.setValue(newValue); // still in the same transaction
} // free lock on global state
cleanup(); // No lock held here either
}

循环终止指示符

3) I have all of the threads accessing "bool run" to check if to continue their loops

while(gs->run){
....
}

run gets set to false if I receive a quit message in the EventManager. Do I need to synchronize that variable at all? Would I set it to volatile?

对于这个应用程序,一个易失但不同步的变量应该没问题。您必须将其声明为 volatile,以防止编译器生成缓存该值的代码,从而隐藏另一个线程的修改。

作为替代方案,您可能想使用 std::atomic变量。

指针间接开销

4) Does constantly dereferencing pointers and such have an impact on performance? eg gs->objects->entitylist.at(2)->move(); Do all those -> and . cause any major slowdown?

这取决于备选方案。在许多情况下,编译器将能够保留例如的值。上面代码中的gs->objects->entitylist.at(2),如果重复使用,就不用反复计算了。总的来说,我认为由于所有这些指针间接寻址而导致的性能损失是次要问题,但这很难确定。

关于c++ - 构建和同步多线程游戏循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12684844/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com