Til now, I have never created a program that uses multithreading, with the exception of a water erosion simulation that split the work into multiple worker threads. In plant sim, I take advantage of the easy-to-use std::thread
header, based on boost::thread
but with a few quality-of-life changes.
In boost::thread,
threads cannot be stored without using shared_ptr or auto_ptr; in the std::thread version, threads are nearly exactly the same as normal objects and can be referred to by reference or by pointer. This doesn’t remove all of the difficulties of multithreading, though–race conditions and concurrency are the main reasons why multithreading is hard. But in this specific case, I don’t need to worry about mutexes or semaphores, since all I want to do is prevent the graphical window from freezing while a long task is executed, and provide a progress bar.
class State_StepSim : public GameState { public: State_StepSim(ALMANAC::Weather* weather, ALMANAC::SoilGrid* soilgrid, bool& updateMap, int number=1); virtual bool Init(){ return true; } virtual void Update(); virtual void Render(TCODConsole *root); virtual void End(); virtual void KeyDown(const int &key, const int &unicode); TCODConsole* loading; protected: int iterations; int itleft; bool& updateMapRef; bool multi; bool abort; ALMANAC::Weather* WeatherModule; ALMANAC::SoilGrid* Grid; std::thread* t; };
A separate state for updating the simulation is created, since I do not want key inputs to be processed while it updates. The class has a pointer to an std::thread, along with its own display screen and some housekeeping variables.
In the constructor, a thread is spawned to run the simulation. In the Update() function, called every time a frame ticks, The SoilGrid’s progress variable (which is simply the number of cells it has already processed) is read and divided by the total number of cells. This information is then drawn to the screen along with a progress bar.
Once progress divided by total cells equals 1, the state calls join()
on the thread and waits for it to finish, then pops itself from the state stack, returning control to the main state.
Because the main thread is always responsive, while the child thread does all the work, the window does not freeze up as GUIs tend to when some function takes too long. Also, because the progress bar doesn’t attempt to change SoilGrid and since its data being outdated by some cycles isn’t important, the usual complications that happen from multithreading don’t exist. All in all, it’s a low-effort high-returns method. Thanks to how easy threading is in C++11, I may find myself using threading in other places as well, such as running a simple fluid sim in the background of the main process.