Structuring and synchronizing a multi-threaded game cycle

I came across an easy puzzle regarding thread safety for my game loop. What I have below is 3 threads (including the main one), designed for collaboration. One for event management (main thread), one for logical and one for rendering. All 3 of these threads exist within their own class, as you can see below. In basic testing, the structure works without problems. This system uses SFML and displays with OpenGL.

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

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

    return 0;
}

However, as you may have noticed, I have a “Gamestate” class that is designed to be used as a container for all resources that should be shared between threads (mainly with LogicManager as a writer and Renderer as a reader). EventManager is mainly for window events only). My questions: (1 and 2 are the most important)

1) Is this a good way to get around things? Does the "global" class Gamestate mean - is it a good idea to use? Is there a better way around this?

2) , Gamestate getters/seters, , , , , , / . , . ?

3) , "bool run", ,

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

run false, quit EventManager. ? ?

4) .. ? gs- > objects- > entitylist.at(2) → move(); → ''. ?

+5
2

1) ? "" Gamestate - ? ?

, , , . , gamestate .

2) , Gamestate getters/seters, , , , , , / . , . ?

. , , , , - . LogicManager Renderer , Gamestate . .

, , . GameStateData GameStateAccess. GameStateData , . GameStateAccess GameStateData . GameStateAccess GameStateData . . , , GameStateAccess .

: , , GameStateAccess, , , . , , , - , , , GameStateAccess , , , .

++ 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) , "bool run", ,

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

run false, quit EventManager. ? ?

, . , , , .

std::atomic .

4) .. ? gs->objects->entitylist.at(2)->move(); -> . ?

. , . gs->objects->entitylist.at(2) , , . , , - , .

+6

? (class Gamestate)

1) ?

.

"" Gamestate - ?

, / .

?

. , . , , . Gamestate .

/

2) , Gamestate / [...]. ?

/. . , , . / , .

while(gs->run)

?

, , . , run false , Gamestate , . , gs->run , .

, , . , , .

4) .. ?

:

  • .

, , . , , , .

+5

All Articles