Multi-threaded game loop
Yes you have added enough explicit locks to make it safe (under normal situations).

But it is not exception safe and not written in C++ style. You are writing the code as if it was Java (maybe). Which is causing all sorts of problems.

Why do you need the start() and stop() functions? They provide no utility and they initialize members that should be initialized in the constructor. Also the threads are started before the object is fully initialized and thus you have a thread running in a function when the object is not initialized correctly (and thus will probably immediately exit).

    void start()
    {
        // Soon as you create a thread 
        // It starts running. So the next thing that is going to be
        // executed is the function `renderFunc()` which also depends
        // on member 'running' which has not been set.
        renderThread = std::unique_ptr&lt;std::thread&gt;(new std::thread(&amp;Test::renderFunc, this));
        updateThread = std::unique_ptr&lt;std::thread&gt;(new std::thread(&amp;Test::updateFunc, this));
        std::cout &lt;&lt; "Threads Created" &lt;&lt; std::endl;


        // You already have running threads that depend on this variable.
        // Yet this is the first time you even set it. Your code is already
        // broken.
        running = true;
    }


The next thing is the dynamic creation of the thread objects. Why. There is no need to dynamically create them. They should be automatic variables (ie non pointer members). The problem is that you need to be very careful with the initialization order.

class Test
{
    volatile bool running;
    std::mutex    mutex;
    std::thread   renderThread;
    std::thread   updateThread;
  public:
    Test()
        : running(false) // Make sure this is first.
        , renderThread(&amp;Test::renderFunc, this)
        , updateThread(&amp;Test::updateFunc, this)
    {
        std::cout &lt;&lt; "Test starting" &lt;&lt; std::endl;
    }
    ~Test()
    {
        std::cout &lt;&lt; "Test finished" &lt;&lt; std::endl;
        updateThread.join();
        renderThread.join();
    }
    inline void quit()
    {
        running = false;
    }


Next thing is your locking and unlocking of the mutex.

    void updateFunc()
    {
        while(running)
        {
            mutex.lock();
            std::cout &lt;&lt; "update" &lt;&lt; std::endl;
            mutex.unlock();
        }
    }


This is not exception safe. Any situation were you do:

{
    lock(x);
    work();
    unlock(x);
}


Where lock/unlock is a combination that must be called as a pair. Then you should be using RAII. This is where you have an object where lock() is called in the constructor and unlock() is called in the destructor. This also makes the code exception safe because the destructor will always be called even  if their are exceptions.

{
    LockObject  locker(x);  // calls lock(x)
    work();
}                           // destructor of locker will call unlock(x)


For this simple situation there is no need to create your own type. There is one defined in the standard scoped locked.

    void updateFunc()
    {
        while(running)
        {
            std::lock_guard&lt;std::mutex&gt; locker(mutex);
            std::cout &lt;&lt; "update" &lt;&lt; std::endl;
        }
    }


Stop using dynamic allocation when you do not need it:

    std::unique_ptr&lt;Test&gt; test = std::unique_ptr&lt;Test&gt;(new Test());

    // Just do this:
    Test    test;