Question

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!

Was it helpful?

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.

OTHER TIPS

It crashes, because in the current version of Boost.Thread, you have to either join() a thread or detach() it - otherwise ~thread would terminate the program. (In earlier versions ~thread used to call detach() automatically.)

So if you don't want to join the thread - just detach it:

boost::thread loadingThread(&ChunkManager::renderChunks,this);
loadingThread.detach();
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top