Search code examples
multithreadingboostmutexhalt

boost::thread execution


I have a class ChunkManager that has a few (supposed to be) asynchronous methods. These methods handle tasks in my game engine such as loading the map blocks (similar to Minecraft) on a different thread so as not to completely halt the main thread (they are lengthy operations) Here is one of those methods:

void ChunkManager::asyncRenderChunks(){

    boost::thread loadingThread(&ChunkManager::renderChunks,this);
}

Where renderChunks looks like:

void ChunkManager::renderChunks(){
activeChunksMutex->lock();
      for(int z=0; z < CHUNK_MAX; z=z+1)
      {
        for(int y=0; y < CHUNK_MAX; y=y+1)
        {
            for(int x=0; x < CHUNK_MAX; x=x+1)
            {

            activeChunks[x][y][z]->Render(scnMgr);

            }
        }
    }
    activeChunksMutex->unlock();
}

This should work, right? However it crashes when this runs. I have a feeling it has to do with what I do with the thread after it's created, because if I put

loadingThread.join();

in the aforementioned method, it works fine, but the main thread is halted because obviously its just waiting for the new thread to finish, effectively bringing me back to square one. Any advice? Sorry if this is a retarded question, I am new to the concept of threads. Thanks.

Update (4/9/2013): I found this gem: http://threadpool.sourceforge.net/ ..and solved my problem!


Solution

  • If you can join the thread, it must be joinable.

    As it says in the documentation:

    When the boost::thread object that represents a thread of execution is destroyed the program terminates if the thread is joinable.

    You created a local thread object and immediately let it go out of scope: it is destroyed when ChunkManager::asyncRenderChunks returns.

    Either:

    • make it a detached (non-joinable) thread

      void ChunkManager::asyncRenderChunks() {
          boost::thread loadingThread(&ChunkManager::renderChunks,this);
          loadingThread.detach();
      }
      
    • or create the thread object elsewhere and keep it alive

      class ChunkManager {
          boost::thread renderingThread;
          bool renderChunkWork;       // work to do flag
          Chunk activeChunks[CHUNK_MAX][CHUNK_MAX][CHUNK_MAX];
          boost::mutex activeChunksMutex;
          boost::condition_variable activeChunksCV;
      
          bool shutdown;              // shutdown flag
      
          void renderChunks() {
              for(int z=0; z < CHUNK_MAX; ++z)
                  for(int y=0; y < CHUNK_MAX; ++y)
                      for(int x=0; x < CHUNK_MAX; ++x)
                          activeChunks[x][y][z]->Render(scnMgr);
          }
      
          void renderChunkThread() {
              boost::unique_lock<boost::mutex> guard(activeChunksMutex);
              while (true) {
                  while (!(renderChunkWork || shutdown))
                      activeChunksCV.wait(guard);
      
                  if (shutdown)
                      break;
                  renderChunks();
                  doRenderChunks = false;
              }
          }
      
      public:
          ChunkManager()
              : loadingThread(&ChunkManager::renderChunkThread, this),
              renderChunkWork(false), shutdown(false)
          {}
      
          ~ChunkManager() {
              { // tell the rendering thread to quit
                  boost::unique_lock<boost::mutex> guard(activeChunksMutex);
                  renderChunkShutdown = true;
                  activeChunksCV.notify_one();
              }
              renderingThread.join()
          }
      
          void asyncRenderChunks() {
              boost::unique_lock<boost::mutex> guard(activeChunksMutex);
              if (!renderChunkWork) {
                  renderChunkWork = true;
                  activeChunksCV.notify_one();
              }
          }
      };
      

    NB. In general, creating threads on-the-fly is less good than creating your threads up-front, and just waking them when there's something to do. It avoids figuring out how to handle a second call to asyncRenderChunks before the last one is complete (start a second thread? block?), and moves the latency associated with thread creation.


    Note on object lifetime

    It's important to realise that in this code:

    void ChunkManager::asyncRenderChunks() {
        SomeType myObject;
    }
    

    the instance myObject will be created and then immediately destroyed.